| /* Copyright 2001 Mike McCormack | |
| * Copyright 2002 Andriy Palamarchuk | |
| * Copyright 2003 Juan Lang | |
| * Copyright 2005,2006 Paul Vriens | |
| * Copyright 2006 Robert Reif | |
| * Copyright 2013 Hans Leidekker for CodeWeavers | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, write to the Free Software | |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | |
| */ | |
| #include "config.h" | |
| #include "wine/port.h" | |
| #include <stdarg.h> | |
| #include <fcntl.h> | |
| #include <errno.h> | |
| #ifdef HAVE_SYS_WAIT_H | |
| #include <sys/wait.h> | |
| #endif | |
| #ifdef HAVE_UNISTD_H | |
| #include <unistd.h> | |
| #endif | |
| #include "ntstatus.h" | |
| #define WIN32_NO_STATUS | |
| #include "windef.h" | |
| #include "winbase.h" | |
| #include "lm.h" | |
| #include "lmaccess.h" | |
| #include "lmat.h" | |
| #include "lmapibuf.h" | |
| #include "lmbrowsr.h" | |
| #include "lmshare.h" | |
| #include "lmwksta.h" | |
| #include "netbios.h" | |
| #include "ifmib.h" | |
| #include "iphlpapi.h" | |
| #include "ntsecapi.h" | |
| #include "winnls.h" | |
| #include "dsrole.h" | |
| #include "dsgetdc.h" | |
| #include "davclnt.h" | |
| #include "wine/debug.h" | |
| #include "wine/library.h" | |
| #include "wine/list.h" | |
| #include "wine/unicode.h" | |
| WINE_DEFAULT_DEBUG_CHANNEL(netapi32); | |
| static char *strdup_unixcp( const WCHAR *str ) | |
| { | |
| char *ret; | |
| int len = WideCharToMultiByte( CP_UNIXCP, 0, str, -1, NULL, 0, NULL, NULL ); | |
| if ((ret = HeapAlloc( GetProcessHeap(), 0, len ))) | |
| WideCharToMultiByte( CP_UNIXCP, 0, str, -1, ret, len, NULL, NULL ); | |
| return ret; | |
| } | |
| #ifdef SONAME_LIBNETAPI | |
| static void *libnetapi_handle; | |
| static void *libnetapi_ctx; | |
| static DWORD (*plibnetapi_init)(void **); | |
| static DWORD (*plibnetapi_free)(void *); | |
| static DWORD (*plibnetapi_set_debuglevel)(void *, const char *); | |
| static DWORD (*plibnetapi_set_username)(void *, const char *); | |
| static DWORD (*plibnetapi_set_password)(void *, const char *); | |
| static NET_API_STATUS (*pNetApiBufferAllocate)(unsigned int, void **); | |
| static NET_API_STATUS (*pNetApiBufferFree)(void *); | |
| static NET_API_STATUS (*pNetServerGetInfo)(const char *, unsigned int, unsigned char **); | |
| static NET_API_STATUS (*pNetShareAdd)(const char *, unsigned int, unsigned char *, unsigned int *); | |
| static NET_API_STATUS (*pNetShareDel)(const char *, const char *, unsigned int); | |
| static NET_API_STATUS (*pNetWkstaGetInfo)(const char *, unsigned int, unsigned char **); | |
| static void destroy_context(void) | |
| { | |
| TRACE( "destroying %p\n", libnetapi_ctx ); | |
| plibnetapi_free( libnetapi_ctx ); | |
| libnetapi_ctx = NULL; | |
| } | |
| static BOOL init_context(void) | |
| { | |
| DWORD status; | |
| if ((status = plibnetapi_init( &libnetapi_ctx ))) | |
| { | |
| ERR( "Failed to initialize context %u\n", status ); | |
| return FALSE; | |
| } | |
| if (TRACE_ON( netapi32 ) && (status = plibnetapi_set_debuglevel( libnetapi_ctx, "10" ))) | |
| { | |
| ERR( "Failed to set debug level %u\n", status ); | |
| destroy_context(); | |
| return FALSE; | |
| } | |
| /* perform an anonymous login by default (avoids a password prompt) */ | |
| if ((status = plibnetapi_set_username( libnetapi_ctx, "Guest" ))) | |
| { | |
| ERR( "Failed to set username %u\n", status ); | |
| destroy_context(); | |
| return FALSE; | |
| } | |
| if ((status = plibnetapi_set_password( libnetapi_ctx, "" ))) | |
| { | |
| ERR( "Failed to set password %u\n", status ); | |
| destroy_context(); | |
| return FALSE; | |
| } | |
| TRACE( "using %p\n", libnetapi_ctx ); | |
| return TRUE; | |
| } | |
| static BOOL libnetapi_init(void) | |
| { | |
| char buf[200]; | |
| if (libnetapi_handle) return TRUE; | |
| if (!(libnetapi_handle = wine_dlopen( SONAME_LIBNETAPI, RTLD_NOW, buf, sizeof(buf) ))) | |
| { | |
| WARN( "Failed to load libnetapi: %s\n", buf ); | |
| return FALSE; | |
| } | |
| #define LOAD_FUNCPTR(f) \ | |
| if (!(p##f = wine_dlsym( libnetapi_handle, #f, buf, sizeof(buf) ))) \ | |
| { \ | |
| ERR( "Failed to load %s: %s\n", #f, buf ); \ | |
| goto error; \ | |
| } | |
| LOAD_FUNCPTR(libnetapi_init) | |
| LOAD_FUNCPTR(libnetapi_free) | |
| LOAD_FUNCPTR(libnetapi_set_debuglevel) | |
| LOAD_FUNCPTR(libnetapi_set_username) | |
| LOAD_FUNCPTR(libnetapi_set_password) | |
| LOAD_FUNCPTR(NetApiBufferAllocate) | |
| LOAD_FUNCPTR(NetApiBufferFree) | |
| LOAD_FUNCPTR(NetServerGetInfo) | |
| LOAD_FUNCPTR(NetShareAdd) | |
| LOAD_FUNCPTR(NetShareDel) | |
| LOAD_FUNCPTR(NetWkstaGetInfo) | |
| #undef LOAD_FUNCPTR | |
| if (init_context()) return TRUE; | |
| error: | |
| wine_dlclose( libnetapi_handle, NULL, 0 ); | |
| libnetapi_handle = NULL; | |
| return FALSE; | |
| } | |
| struct server_info_101 | |
| { | |
| unsigned int sv101_platform_id; | |
| const char *sv101_name; | |
| unsigned int sv101_version_major; | |
| unsigned int sv101_version_minor; | |
| unsigned int sv101_type; | |
| const char *sv101_comment; | |
| }; | |
| static NET_API_STATUS server_info_101_from_samba( const unsigned char *buf, BYTE **bufptr ) | |
| { | |
| SERVER_INFO_101 *ret; | |
| struct server_info_101 *info = (struct server_info_101 *)buf; | |
| DWORD len = 0; | |
| WCHAR *ptr; | |
| if (info->sv101_name) len += MultiByteToWideChar( CP_UNIXCP, 0, info->sv101_name, -1, NULL, 0 ); | |
| if (info->sv101_comment) len += MultiByteToWideChar( CP_UNIXCP, 0, info->sv101_comment, -1, NULL, 0 ); | |
| if (!(ret = HeapAlloc( GetProcessHeap(), 0, sizeof(*ret) + (len * sizeof(WCHAR) )))) | |
| return ERROR_OUTOFMEMORY; | |
| ptr = (WCHAR *)(ret + 1); | |
| ret->sv101_platform_id = info->sv101_platform_id; | |
| if (!info->sv101_name) ret->sv101_name = NULL; | |
| else | |
| { | |
| ret->sv101_name = ptr; | |
| ptr += MultiByteToWideChar( CP_UNIXCP, 0, info->sv101_name, -1, ptr, len ); | |
| } | |
| ret->sv101_version_major = info->sv101_version_major; | |
| ret->sv101_version_minor = info->sv101_version_minor; | |
| ret->sv101_type = info->sv101_type; | |
| if (!info->sv101_comment) ret->sv101_comment = NULL; | |
| else | |
| { | |
| ret->sv101_comment = ptr; | |
| MultiByteToWideChar( CP_UNIXCP, 0, info->sv101_comment, -1, ptr, len ); | |
| } | |
| *bufptr = (BYTE *)ret; | |
| return NERR_Success; | |
| } | |
| static NET_API_STATUS server_info_from_samba( DWORD level, const unsigned char *buf, BYTE **bufptr ) | |
| { | |
| switch (level) | |
| { | |
| case 101: return server_info_101_from_samba( buf, bufptr ); | |
| default: | |
| FIXME( "level %u not supported\n", level ); | |
| return ERROR_NOT_SUPPORTED; | |
| } | |
| } | |
| static NET_API_STATUS server_getinfo( LMSTR servername, DWORD level, LPBYTE *bufptr ) | |
| { | |
| NET_API_STATUS status; | |
| char *server = NULL; | |
| unsigned char *buf = NULL; | |
| if (servername && !(server = strdup_unixcp( servername ))) return ERROR_OUTOFMEMORY; | |
| status = pNetServerGetInfo( server, level, &buf ); | |
| HeapFree( GetProcessHeap(), 0, server ); | |
| if (!status) | |
| { | |
| status = server_info_from_samba( level, buf, bufptr ); | |
| pNetApiBufferFree( buf ); | |
| } | |
| return status; | |
| } | |
| struct share_info_2 | |
| { | |
| const char *shi2_netname; | |
| unsigned int shi2_type; | |
| const char *shi2_remark; | |
| unsigned int shi2_permissions; | |
| unsigned int shi2_max_uses; | |
| unsigned int shi2_current_uses; | |
| const char *shi2_path; | |
| const char *shi2_passwd; | |
| }; | |
| static NET_API_STATUS share_info_2_to_samba( const BYTE *buf, unsigned char **bufptr ) | |
| { | |
| struct share_info_2 *ret; | |
| SHARE_INFO_2 *info = (SHARE_INFO_2 *)buf; | |
| DWORD len = 0; | |
| char *ptr; | |
| if (info->shi2_netname) | |
| len += WideCharToMultiByte( CP_UNIXCP, 0, info->shi2_netname, -1, NULL, 0, NULL, NULL ); | |
| if (info->shi2_remark) | |
| len += WideCharToMultiByte( CP_UNIXCP, 0, info->shi2_remark, -1, NULL, 0, NULL, NULL ); | |
| if (info->shi2_path) | |
| len += WideCharToMultiByte( CP_UNIXCP, 0, info->shi2_path, -1, NULL, 0, NULL, NULL ); | |
| if (info->shi2_passwd) | |
| len += WideCharToMultiByte( CP_UNIXCP, 0, info->shi2_passwd, -1, NULL, 0, NULL, NULL ); | |
| if (!(ret = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*ret) + len ))) | |
| return ERROR_OUTOFMEMORY; | |
| ptr = (char *)(ret + 1); | |
| if (!info->shi2_netname) ret->shi2_netname = NULL; | |
| else | |
| { | |
| ret->shi2_netname = ptr; | |
| ptr += WideCharToMultiByte( CP_UNIXCP, 0, info->shi2_netname, -1, ptr, len, NULL, NULL ); | |
| } | |
| ret->shi2_type = info->shi2_type; | |
| if (!info->shi2_remark) ret->shi2_remark = NULL; | |
| else | |
| { | |
| ret->shi2_remark = ptr; | |
| ptr += WideCharToMultiByte( CP_UNIXCP, 0, info->shi2_remark, -1, ptr, len, NULL, NULL ); | |
| } | |
| ret->shi2_permissions = info->shi2_permissions; | |
| ret->shi2_max_uses = info->shi2_max_uses; | |
| ret->shi2_current_uses = info->shi2_current_uses; | |
| if (!info->shi2_path) ret->shi2_path = NULL; | |
| else | |
| { | |
| ret->shi2_path = ptr; | |
| ptr += WideCharToMultiByte( CP_UNIXCP, 0, info->shi2_path, -1, ptr, len, NULL, NULL ); | |
| } | |
| if (!info->shi2_passwd) ret->shi2_passwd = NULL; | |
| else | |
| { | |
| ret->shi2_passwd = ptr; | |
| WideCharToMultiByte( CP_UNIXCP, 0, info->shi2_passwd, -1, ptr, len, NULL, NULL ); | |
| } | |
| *bufptr = (unsigned char *)ret; | |
| return NERR_Success; | |
| } | |
| struct sid | |
| { | |
| unsigned char sid_rev_num; | |
| unsigned char num_auths; | |
| unsigned char id_auth[6]; | |
| unsigned int sub_auths[15]; | |
| }; | |
| enum ace_type | |
| { | |
| ACE_TYPE_ACCESS_ALLOWED, | |
| ACE_TYPE_ACCESS_DENIED, | |
| ACE_TYPE_SYSTEM_AUDIT, | |
| ACE_TYPE_SYSTEM_ALARM, | |
| ACE_TYPE_ALLOWED_COMPOUND, | |
| ACE_TYPE_ACCESS_ALLOWED_OBJECT, | |
| ACE_TYPE_ACCESS_DENIED_OBJECT, | |
| ACE_TYPE_SYSTEM_AUDIT_OBJECT, | |
| ACE_TYPE_SYSTEM_ALARM_OBJECT | |
| }; | |
| #define SEC_ACE_FLAG_OBJECT_INHERIT 0x01 | |
| #define SEC_ACE_FLAG_CONTAINER_INHERIT 0x02 | |
| #define SEC_ACE_FLAG_NO_PROPAGATE_INHERIT 0x04 | |
| #define SEC_ACE_FLAG_INHERIT_ONLY 0x08 | |
| #define SEC_ACE_FLAG_INHERITED_ACE 0x10 | |
| #define SEC_ACE_FLAG_SUCCESSFUL_ACCESS 0x40 | |
| #define SEC_ACE_FLAG_FAILED_ACCESS 0x80 | |
| struct guid | |
| { | |
| unsigned int time_low; | |
| unsigned short time_mid; | |
| unsigned short time_hi_and_version; | |
| unsigned char clock_seq[2]; | |
| unsigned char node[6]; | |
| }; | |
| union ace_object_type | |
| { | |
| struct guid type; | |
| }; | |
| union ace_object_inherited_type | |
| { | |
| struct guid inherited_type; | |
| }; | |
| struct ace_object | |
| { | |
| unsigned int flags; | |
| union ace_object_type type; | |
| union ace_object_inherited_type inherited_type; | |
| }; | |
| union ace_object_ctr | |
| { | |
| struct ace_object object; | |
| }; | |
| struct ace | |
| { | |
| enum ace_type type; | |
| unsigned char flags; | |
| unsigned short size; | |
| unsigned int access_mask; | |
| union ace_object_ctr object; | |
| struct sid trustee; | |
| }; | |
| enum acl_revision | |
| { | |
| ACL_REVISION_NT4 = 2, | |
| ACL_REVISION_ADS = 4 | |
| }; | |
| struct acl | |
| { | |
| enum acl_revision revision; | |
| unsigned short size; | |
| unsigned int num_aces; | |
| struct ace *aces; | |
| }; | |
| enum security_descriptor_revision | |
| { | |
| SECURITY_DESCRIPTOR_REVISION_1 = 1 | |
| }; | |
| #define SEC_DESC_OWNER_DEFAULTED 0x0001 | |
| #define SEC_DESC_GROUP_DEFAULTED 0x0002 | |
| #define SEC_DESC_DACL_PRESENT 0x0004 | |
| #define SEC_DESC_DACL_DEFAULTED 0x0008 | |
| #define SEC_DESC_SACL_PRESENT 0x0010 | |
| #define SEC_DESC_SACL_DEFAULTED 0x0020 | |
| #define SEC_DESC_DACL_TRUSTED 0x0040 | |
| #define SEC_DESC_SERVER_SECURITY 0x0080 | |
| #define SEC_DESC_DACL_AUTO_INHERIT_REQ 0x0100 | |
| #define SEC_DESC_SACL_AUTO_INHERIT_REQ 0x0200 | |
| #define SEC_DESC_DACL_AUTO_INHERITED 0x0400 | |
| #define SEC_DESC_SACL_AUTO_INHERITED 0x0800 | |
| #define SEC_DESC_DACL_PROTECTED 0x1000 | |
| #define SEC_DESC_SACL_PROTECTED 0x2000 | |
| #define SEC_DESC_RM_CONTROL_VALID 0x4000 | |
| #define SEC_DESC_SELF_RELATIVE 0x8000 | |
| struct security_descriptor | |
| { | |
| enum security_descriptor_revision revision; | |
| unsigned short type; | |
| struct sid *owner_sid; | |
| struct sid *group_sid; | |
| struct acl *sacl; | |
| struct acl *dacl; | |
| }; | |
| struct share_info_502 | |
| { | |
| const char *shi502_netname; | |
| unsigned int shi502_type; | |
| const char *shi502_remark; | |
| unsigned int shi502_permissions; | |
| unsigned int shi502_max_uses; | |
| unsigned int shi502_current_uses; | |
| const char *shi502_path; | |
| const char *shi502_passwd; | |
| unsigned int shi502_reserved; | |
| struct security_descriptor *shi502_security_descriptor; | |
| }; | |
| static unsigned short sd_control_to_samba( SECURITY_DESCRIPTOR_CONTROL control ) | |
| { | |
| unsigned short ret = 0; | |
| if (control & SE_OWNER_DEFAULTED) ret |= SEC_DESC_OWNER_DEFAULTED; | |
| if (control & SE_GROUP_DEFAULTED) ret |= SEC_DESC_GROUP_DEFAULTED; | |
| if (control & SE_DACL_PRESENT) ret |= SEC_DESC_DACL_PRESENT; | |
| if (control & SE_DACL_DEFAULTED) ret |= SEC_DESC_DACL_DEFAULTED; | |
| if (control & SE_SACL_PRESENT) ret |= SEC_DESC_SACL_PRESENT; | |
| if (control & SE_SACL_DEFAULTED) ret |= SEC_DESC_SACL_DEFAULTED; | |
| if (control & SE_DACL_AUTO_INHERIT_REQ) ret |= SEC_DESC_DACL_AUTO_INHERIT_REQ; | |
| if (control & SE_SACL_AUTO_INHERIT_REQ) ret |= SEC_DESC_SACL_AUTO_INHERIT_REQ; | |
| if (control & SE_DACL_AUTO_INHERITED) ret |= SEC_DESC_DACL_AUTO_INHERITED; | |
| if (control & SE_SACL_AUTO_INHERITED) ret |= SEC_DESC_SACL_AUTO_INHERITED; | |
| if (control & SE_DACL_PROTECTED) ret |= SEC_DESC_DACL_PROTECTED; | |
| if (control & SE_SACL_PROTECTED) ret |= SEC_DESC_SACL_PROTECTED; | |
| if (control & SE_RM_CONTROL_VALID) ret |= SEC_DESC_RM_CONTROL_VALID; | |
| return ret; | |
| } | |
| static NET_API_STATUS sid_to_samba( const SID *src, struct sid *dst ) | |
| { | |
| unsigned int i; | |
| if (src->Revision != 1) | |
| { | |
| ERR( "unknown revision %u\n", src->Revision ); | |
| return ERROR_UNKNOWN_REVISION; | |
| } | |
| if (src->SubAuthorityCount > SID_MAX_SUB_AUTHORITIES) | |
| { | |
| WARN( "invalid subauthority count %u\n", src->SubAuthorityCount ); | |
| return ERROR_INVALID_PARAMETER; | |
| } | |
| dst->sid_rev_num = SECURITY_DESCRIPTOR_REVISION_1; | |
| dst->num_auths = src->SubAuthorityCount; | |
| for (i = 0; i < 6; i++) dst->id_auth[i] = src->IdentifierAuthority.Value[i]; | |
| for (i = 0; i < dst->num_auths; i++) dst->sub_auths[i] = src->SubAuthority[i]; | |
| return NERR_Success; | |
| } | |
| static enum ace_type ace_type_to_samba( BYTE type ) | |
| { | |
| switch (type) | |
| { | |
| case ACCESS_ALLOWED_ACE_TYPE: return ACE_TYPE_ACCESS_ALLOWED; | |
| case ACCESS_DENIED_ACE_TYPE: return ACE_TYPE_ACCESS_DENIED; | |
| case SYSTEM_AUDIT_ACE_TYPE: return ACE_TYPE_SYSTEM_AUDIT; | |
| case SYSTEM_ALARM_ACE_TYPE: return ACE_TYPE_SYSTEM_ALARM; | |
| default: | |
| ERR( "unhandled type %u\n", type ); | |
| return 0; | |
| } | |
| } | |
| static unsigned char ace_flags_to_samba( BYTE flags ) | |
| { | |
| static const BYTE known_flags = | |
| OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | NO_PROPAGATE_INHERIT_ACE | | |
| INHERIT_ONLY_ACE | INHERITED_ACE | SUCCESSFUL_ACCESS_ACE_FLAG | FAILED_ACCESS_ACE_FLAG; | |
| unsigned char ret = 0; | |
| if (flags & ~known_flags) | |
| { | |
| ERR( "unknown flags %x\n", flags & ~known_flags ); | |
| return 0; | |
| } | |
| if (flags & OBJECT_INHERIT_ACE) ret |= SEC_ACE_FLAG_OBJECT_INHERIT; | |
| if (flags & CONTAINER_INHERIT_ACE) ret |= SEC_ACE_FLAG_NO_PROPAGATE_INHERIT; | |
| if (flags & NO_PROPAGATE_INHERIT_ACE) ret |= SEC_ACE_FLAG_NO_PROPAGATE_INHERIT; | |
| if (flags & INHERIT_ONLY_ACE) ret |= SEC_ACE_FLAG_INHERIT_ONLY; | |
| if (flags & INHERITED_ACE) ret |= SEC_ACE_FLAG_INHERITED_ACE; | |
| if (flags & SUCCESSFUL_ACCESS_ACE_FLAG) ret |= SEC_ACE_FLAG_SUCCESSFUL_ACCESS; | |
| if (flags & FAILED_ACCESS_ACE_FLAG) ret |= SEC_ACE_FLAG_FAILED_ACCESS; | |
| return ret; | |
| } | |
| #define GENERIC_ALL_ACCESS (1u << 28) | |
| #define GENERIC_EXECUTE_ACCESS (1u << 29) | |
| #define GENERIC_WRITE_ACCESS (1u << 30) | |
| #define GENERIC_READ_ACCESS (1u << 31) | |
| static unsigned int access_mask_to_samba( DWORD mask ) | |
| { | |
| static const DWORD known_rights = | |
| GENERIC_ALL | GENERIC_EXECUTE | GENERIC_WRITE | GENERIC_READ; | |
| unsigned int ret = 0; | |
| if (mask & ~known_rights) | |
| { | |
| ERR( "unknown rights %x\n", mask & ~known_rights ); | |
| return 0; | |
| } | |
| if (mask & GENERIC_ALL) ret |= GENERIC_ALL_ACCESS; | |
| if (mask & GENERIC_EXECUTE) ret |= GENERIC_EXECUTE_ACCESS; | |
| if (mask & GENERIC_WRITE) ret |= GENERIC_WRITE_ACCESS; | |
| if (mask & GENERIC_READ) ret |= GENERIC_READ_ACCESS; | |
| return ret; | |
| } | |
| static NET_API_STATUS ace_to_samba( const ACE_HEADER *src, struct ace *dst ) | |
| { | |
| dst->type = ace_type_to_samba( src->AceType ); | |
| dst->flags = ace_flags_to_samba( src->AceFlags ); | |
| dst->size = sizeof(*dst); | |
| switch (src->AceType) | |
| { | |
| case ACCESS_ALLOWED_ACE_TYPE: | |
| { | |
| ACCESS_ALLOWED_ACE *ace = (ACCESS_ALLOWED_ACE *)src; | |
| dst->access_mask = access_mask_to_samba( ace->Mask ); | |
| memset( &dst->object, 0, sizeof(dst->object) ); | |
| sid_to_samba( (const SID *)&ace->SidStart, &dst->trustee ); | |
| break; | |
| } | |
| case ACCESS_DENIED_ACE_TYPE: | |
| { | |
| ACCESS_DENIED_ACE *ace = (ACCESS_DENIED_ACE *)src; | |
| dst->access_mask = access_mask_to_samba( ace->Mask ); | |
| memset( &dst->object, 0, sizeof(dst->object) ); | |
| sid_to_samba( (const SID *)&ace->SidStart, &dst->trustee ); | |
| break; | |
| } | |
| case SYSTEM_AUDIT_ACE_TYPE: | |
| { | |
| SYSTEM_AUDIT_ACE *ace = (SYSTEM_AUDIT_ACE *)src; | |
| dst->access_mask = access_mask_to_samba( ace->Mask ); | |
| memset( &dst->object, 0, sizeof(dst->object) ); | |
| sid_to_samba( (const SID *)&ace->SidStart, &dst->trustee ); | |
| break; | |
| } | |
| case SYSTEM_ALARM_ACE_TYPE: | |
| { | |
| SYSTEM_ALARM_ACE *ace = (SYSTEM_ALARM_ACE *)src; | |
| dst->access_mask = access_mask_to_samba( ace->Mask ); | |
| memset( &dst->object, 0, sizeof(dst->object) ); | |
| sid_to_samba( (const SID *)&ace->SidStart, &dst->trustee ); | |
| break; | |
| } | |
| default: | |
| ERR( "unhandled type %u\n", src->AceType ); | |
| return ERROR_INVALID_PARAMETER; | |
| } | |
| return NERR_Success; | |
| } | |
| static NET_API_STATUS acl_to_samba( const ACL *src, struct acl *dst ) | |
| { | |
| NET_API_STATUS status; | |
| ACE_HEADER *src_ace; | |
| unsigned int i; | |
| switch (src->AclRevision) | |
| { | |
| case ACL_REVISION4: | |
| dst->revision = ACL_REVISION_ADS; | |
| break; | |
| default: | |
| ERR( "unkhandled revision %u\n", src->AclRevision ); | |
| return ERROR_UNKNOWN_REVISION; | |
| } | |
| dst->size = sizeof(*dst); | |
| src_ace = (ACE_HEADER *)(src + 1); | |
| dst->aces = (struct ace *)(dst + 1); | |
| for (i = 0; i < src->AceCount; i++) | |
| { | |
| if ((status = ace_to_samba( src_ace, &dst->aces[i] ))) return status; | |
| src_ace = (ACE_HEADER *)((char *)src_ace + src_ace->AceSize); | |
| dst->size += dst->aces[i].size; | |
| } | |
| return NERR_Success; | |
| } | |
| #define SELF_RELATIVE_FIELD(sd,field)\ | |
| ((char *)(sd) + ((SECURITY_DESCRIPTOR_RELATIVE *)(sd))->field) | |
| static NET_API_STATUS sd_to_samba( const SECURITY_DESCRIPTOR *src, struct security_descriptor *dst ) | |
| { | |
| NET_API_STATUS status; | |
| const SID *owner, *group; | |
| const ACL *dacl, *sacl; | |
| unsigned int offset = sizeof(*dst); | |
| if (src->Revision != SECURITY_DESCRIPTOR_REVISION1) | |
| return ERROR_UNKNOWN_REVISION; | |
| dst->revision = SECURITY_DESCRIPTOR_REVISION_1; | |
| dst->type = sd_control_to_samba( src->Control ); | |
| if (src->Control & SE_SELF_RELATIVE) | |
| { | |
| if (!src->Owner) dst->owner_sid = NULL; | |
| else | |
| { | |
| dst->owner_sid = (struct sid *)((char *)dst + offset); | |
| owner = (const SID *)SELF_RELATIVE_FIELD( src, Owner ); | |
| if ((status = sid_to_samba( owner, dst->owner_sid ))) return status; | |
| offset += sizeof(struct sid); | |
| } | |
| if (!src->Group) dst->group_sid = NULL; | |
| else | |
| { | |
| dst->group_sid = (struct sid *)((char *)dst + offset); | |
| group = (const SID *)SELF_RELATIVE_FIELD( src, Group ); | |
| if ((status = sid_to_samba( group, dst->group_sid ))) return status; | |
| offset += sizeof(struct sid); | |
| } | |
| if (!(src->Control & SE_SACL_PRESENT)) dst->sacl = NULL; | |
| else | |
| { | |
| dst->sacl = (struct acl *)((char *)dst + offset); | |
| sacl = (const ACL *)SELF_RELATIVE_FIELD( src, Sacl ); | |
| if ((status = acl_to_samba( sacl, dst->sacl ))) return status; | |
| offset += dst->sacl->size; | |
| } | |
| if (!(src->Control & SE_DACL_PRESENT)) dst->dacl = NULL; | |
| else | |
| { | |
| dst->dacl = (struct acl *)((char *)dst + offset); | |
| dacl = (const ACL *)SELF_RELATIVE_FIELD( src, Dacl ); | |
| if ((status = acl_to_samba( dacl, dst->dacl ))) return status; | |
| } | |
| } | |
| else | |
| { | |
| if (!src->Owner) dst->owner_sid = NULL; | |
| else | |
| { | |
| dst->owner_sid = (struct sid *)((char *)dst + offset); | |
| if ((status = sid_to_samba( src->Owner, dst->owner_sid ))) return status; | |
| offset += sizeof(struct sid); | |
| } | |
| if (!src->Group) dst->group_sid = NULL; | |
| else | |
| { | |
| dst->group_sid = (struct sid *)((char *)dst + offset); | |
| if ((status = sid_to_samba( src->Group, dst->group_sid ))) return status; | |
| offset += sizeof(struct sid); | |
| } | |
| if (!(src->Control & SE_SACL_PRESENT)) dst->sacl = NULL; | |
| else | |
| { | |
| dst->sacl = (struct acl *)((char *)dst + offset); | |
| if ((status = acl_to_samba( src->Sacl, dst->sacl ))) return status; | |
| offset += dst->sacl->size; | |
| } | |
| if (!(src->Control & SE_DACL_PRESENT)) dst->dacl = NULL; | |
| else | |
| { | |
| dst->dacl = (struct acl *)((char *)dst + offset); | |
| if ((status = acl_to_samba( src->Dacl, dst->dacl ))) return status; | |
| } | |
| } | |
| return NERR_Success; | |
| } | |
| static unsigned int sd_to_samba_size( const SECURITY_DESCRIPTOR *sd ) | |
| { | |
| unsigned int ret = sizeof(struct security_descriptor); | |
| if (sd->Owner) ret += sizeof(struct sid); | |
| if (sd->Group) ret += sizeof(struct sid); | |
| if (sd->Control & SE_SACL_PRESENT) | |
| ret += sizeof(struct acl) + sd->Sacl->AceCount * sizeof(struct ace); | |
| if (sd->Control & SE_DACL_PRESENT) | |
| ret += sizeof(struct acl) + sd->Dacl->AceCount * sizeof(struct ace); | |
| return ret; | |
| } | |
| static NET_API_STATUS share_info_502_to_samba( const BYTE *buf, unsigned char **bufptr ) | |
| { | |
| NET_API_STATUS status; | |
| struct share_info_502 *ret; | |
| SHARE_INFO_502 *info = (SHARE_INFO_502 *)buf; | |
| DWORD len = 0, size = 0; | |
| char *ptr; | |
| *bufptr = NULL; | |
| if (info->shi502_netname) | |
| len += WideCharToMultiByte( CP_UNIXCP, 0, info->shi502_netname, -1, NULL, 0, NULL, NULL ); | |
| if (info->shi502_remark) | |
| len += WideCharToMultiByte( CP_UNIXCP, 0, info->shi502_remark, -1, NULL, 0, NULL, NULL ); | |
| if (info->shi502_path) | |
| len += WideCharToMultiByte( CP_UNIXCP, 0, info->shi502_path, -1, NULL, 0, NULL, NULL ); | |
| if (info->shi502_passwd) | |
| len += WideCharToMultiByte( CP_UNIXCP, 0, info->shi502_passwd, -1, NULL, 0, NULL, NULL ); | |
| if (info->shi502_security_descriptor) | |
| size = sd_to_samba_size( info->shi502_security_descriptor ); | |
| if (!(ret = HeapAlloc( GetProcessHeap(), 0, sizeof(*ret) + (len * sizeof(WCHAR)) + size ))) | |
| return ERROR_OUTOFMEMORY; | |
| ptr = (char *)(ret + 1); | |
| if (!info->shi502_netname) ret->shi502_netname = NULL; | |
| else | |
| { | |
| ret->shi502_netname = ptr; | |
| ptr += WideCharToMultiByte( CP_UNIXCP, 0, info->shi502_netname, -1, ptr, len, NULL, NULL ); | |
| } | |
| ret->shi502_type = info->shi502_type; | |
| if (!info->shi502_remark) ret->shi502_remark = NULL; | |
| else | |
| { | |
| ret->shi502_remark = ptr; | |
| ptr += WideCharToMultiByte( CP_UNIXCP, 0, info->shi502_remark, -1, ptr, len, NULL, NULL ); | |
| } | |
| ret->shi502_permissions = info->shi502_permissions; | |
| ret->shi502_max_uses = info->shi502_max_uses; | |
| ret->shi502_current_uses = info->shi502_current_uses; | |
| if (!info->shi502_path) ret->shi502_path = NULL; | |
| else | |
| { | |
| ret->shi502_path = ptr; | |
| ptr += WideCharToMultiByte( CP_UNIXCP, 0, info->shi502_path, -1, ptr, len, NULL, NULL ); | |
| } | |
| if (!info->shi502_passwd) ret->shi502_passwd = NULL; | |
| else | |
| { | |
| ret->shi502_passwd = ptr; | |
| ptr += WideCharToMultiByte( CP_UNIXCP, 0, info->shi502_passwd, -1, ptr, len, NULL, NULL ); | |
| } | |
| ret->shi502_reserved = info->shi502_reserved; | |
| if (!info->shi502_security_descriptor) ret->shi502_security_descriptor = NULL; | |
| else | |
| { | |
| status = sd_to_samba( info->shi502_security_descriptor, (struct security_descriptor *)ptr ); | |
| if (status) | |
| { | |
| HeapFree( GetProcessHeap(), 0, ret ); | |
| return status; | |
| } | |
| ret->shi502_security_descriptor = (struct security_descriptor *)ptr; | |
| } | |
| *bufptr = (unsigned char *)ret; | |
| return NERR_Success; | |
| } | |
| static NET_API_STATUS share_info_to_samba( DWORD level, const BYTE *buf, unsigned char **bufptr ) | |
| { | |
| switch (level) | |
| { | |
| case 2: return share_info_2_to_samba( buf, bufptr ); | |
| case 502: return share_info_502_to_samba( buf, bufptr ); | |
| default: | |
| FIXME( "level %u not supported\n", level ); | |
| return ERROR_NOT_SUPPORTED; | |
| } | |
| } | |
| static NET_API_STATUS share_add( LMSTR servername, DWORD level, LPBYTE buf, LPDWORD parm_err ) | |
| { | |
| char *server = NULL; | |
| unsigned char *info; | |
| NET_API_STATUS status; | |
| if (servername && !(server = strdup_unixcp( servername ))) return ERROR_OUTOFMEMORY; | |
| status = share_info_to_samba( level, buf, &info ); | |
| if (!status) | |
| { | |
| unsigned int err; | |
| status = pNetShareAdd( server, level, info, &err ); | |
| HeapFree( GetProcessHeap(), 0, info ); | |
| if (parm_err) *parm_err = err; | |
| } | |
| HeapFree( GetProcessHeap(), 0, server ); | |
| return status; | |
| } | |
| static NET_API_STATUS share_del( LMSTR servername, LMSTR netname, DWORD reserved ) | |
| { | |
| char *server = NULL, *share; | |
| NET_API_STATUS status; | |
| if (servername && !(server = strdup_unixcp( servername ))) return ERROR_OUTOFMEMORY; | |
| if (!(share = strdup_unixcp( netname ))) | |
| { | |
| HeapFree( GetProcessHeap(), 0, server ); | |
| return ERROR_OUTOFMEMORY; | |
| } | |
| status = pNetShareDel( server, share, reserved ); | |
| HeapFree( GetProcessHeap(), 0, server ); | |
| HeapFree( GetProcessHeap(), 0, share ); | |
| return status; | |
| } | |
| struct wksta_info_100 | |
| { | |
| unsigned int wki100_platform_id; | |
| const char *wki100_computername; | |
| const char *wki100_langroup; | |
| unsigned int wki100_ver_major; | |
| unsigned int wki100_ver_minor; | |
| }; | |
| static NET_API_STATUS wksta_info_100_from_samba( const unsigned char *buf, BYTE **bufptr ) | |
| { | |
| WKSTA_INFO_100 *ret; | |
| struct wksta_info_100 *info = (struct wksta_info_100 *)buf; | |
| DWORD len = 0; | |
| WCHAR *ptr; | |
| if (info->wki100_computername) | |
| len += MultiByteToWideChar( CP_UNIXCP, 0, info->wki100_computername, -1, NULL, 0 ); | |
| if (info->wki100_langroup) | |
| len += MultiByteToWideChar( CP_UNIXCP, 0, info->wki100_langroup, -1, NULL, 0 ); | |
| if (!(ret = HeapAlloc( GetProcessHeap(), 0, sizeof(*ret) + (len * sizeof(WCHAR) )))) | |
| return ERROR_OUTOFMEMORY; | |
| ptr = (WCHAR *)(ret + 1); | |
| ret->wki100_platform_id = info->wki100_platform_id; | |
| if (!info->wki100_computername) ret->wki100_computername = NULL; | |
| else | |
| { | |
| ret->wki100_computername = ptr; | |
| ptr += MultiByteToWideChar( CP_UNIXCP, 0, info->wki100_computername, -1, ptr, len ); | |
| } | |
| if (!info->wki100_langroup) ret->wki100_langroup = NULL; | |
| else | |
| { | |
| ret->wki100_langroup = ptr; | |
| MultiByteToWideChar( CP_UNIXCP, 0, info->wki100_langroup, -1, ptr, len ); | |
| } | |
| ret->wki100_ver_major = info->wki100_ver_major; | |
| ret->wki100_ver_minor = info->wki100_ver_minor; | |
| *bufptr = (BYTE *)ret; | |
| return NERR_Success; | |
| } | |
| static NET_API_STATUS wksta_info_from_samba( DWORD level, const unsigned char *buf, BYTE **bufptr ) | |
| { | |
| switch (level) | |
| { | |
| case 100: return wksta_info_100_from_samba( buf, bufptr ); | |
| default: | |
| FIXME( "level %u not supported\n", level ); | |
| return ERROR_NOT_SUPPORTED; | |
| } | |
| } | |
| static NET_API_STATUS wksta_getinfo( LMSTR servername, DWORD level, LPBYTE *bufptr ) | |
| { | |
| NET_API_STATUS status; | |
| char *wksta = NULL; | |
| unsigned char *buf = NULL; | |
| if (servername && !(wksta = strdup_unixcp( servername ))) return ERROR_OUTOFMEMORY; | |
| status = pNetWkstaGetInfo( wksta, level, &buf ); | |
| HeapFree( GetProcessHeap(), 0, wksta ); | |
| if (!status) | |
| { | |
| status = wksta_info_from_samba( level, buf, bufptr ); | |
| pNetApiBufferFree( buf ); | |
| } | |
| return status; | |
| } | |
| #else | |
| static BOOL libnetapi_init(void) | |
| { | |
| return FALSE; | |
| } | |
| static NET_API_STATUS server_getinfo( LMSTR servername, DWORD level, LPBYTE *bufptr ) | |
| { | |
| ERR( "\n" ); | |
| return ERROR_NOT_SUPPORTED; | |
| } | |
| static NET_API_STATUS share_add( LMSTR servername, DWORD level, LPBYTE buf, LPDWORD parm_err ) | |
| { | |
| ERR( "\n" ); | |
| return ERROR_NOT_SUPPORTED; | |
| } | |
| static NET_API_STATUS share_del( LMSTR servername, LMSTR netname, DWORD reserved ) | |
| { | |
| ERR( "\n" ); | |
| return ERROR_NOT_SUPPORTED; | |
| } | |
| static NET_API_STATUS wksta_getinfo( LMSTR servername, DWORD level, LPBYTE *bufptr ) | |
| { | |
| ERR( "\n" ); | |
| return ERROR_NOT_SUPPORTED; | |
| } | |
| #endif /* SONAME_LIBNETAPI */ | |
| /************************************************************ | |
| * NETAPI_IsLocalComputer | |
| * | |
| * Checks whether the server name indicates local machine. | |
| */ | |
| static BOOL NETAPI_IsLocalComputer( LMCSTR name ) | |
| { | |
| WCHAR buf[MAX_COMPUTERNAME_LENGTH + 1]; | |
| DWORD size = sizeof(buf) / sizeof(buf[0]); | |
| BOOL ret; | |
| if (!name || !name[0]) return TRUE; | |
| ret = GetComputerNameW( buf, &size ); | |
| if (ret && name[0] == '\\' && name[1] == '\\') name += 2; | |
| return ret && !strcmpiW( name, buf ); | |
| } | |
| BOOL WINAPI DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) | |
| { | |
| TRACE("%p,%x,%p\n", hinstDLL, fdwReason, lpvReserved); | |
| switch (fdwReason) { | |
| case DLL_PROCESS_ATTACH: | |
| DisableThreadLibraryCalls(hinstDLL); | |
| NetBIOSInit(); | |
| NetBTInit(); | |
| break; | |
| case DLL_PROCESS_DETACH: | |
| if (lpvReserved) break; | |
| NetBIOSShutdown(); | |
| break; | |
| } | |
| return TRUE; | |
| } | |
| /************************************************************ | |
| * NetServerEnum (NETAPI32.@) | |
| */ | |
| NET_API_STATUS WINAPI NetServerEnum( | |
| LMCSTR servername, | |
| DWORD level, | |
| LPBYTE* bufptr, | |
| DWORD prefmaxlen, | |
| LPDWORD entriesread, | |
| LPDWORD totalentries, | |
| DWORD servertype, | |
| LMCSTR domain, | |
| LPDWORD resume_handle | |
| ) | |
| { | |
| FIXME("Stub (%s %d %p %d %p %p %d %s %p)\n", debugstr_w(servername), | |
| level, bufptr, prefmaxlen, entriesread, totalentries, servertype, | |
| debugstr_w(domain), resume_handle); | |
| return ERROR_NO_BROWSER_SERVERS_FOUND; | |
| } | |
| /************************************************************ | |
| * NetServerEnumEx (NETAPI32.@) | |
| */ | |
| NET_API_STATUS WINAPI NetServerEnumEx( | |
| LMCSTR ServerName, | |
| DWORD Level, | |
| LPBYTE *Bufptr, | |
| DWORD PrefMaxlen, | |
| LPDWORD EntriesRead, | |
| LPDWORD totalentries, | |
| DWORD servertype, | |
| LMCSTR domain, | |
| LMCSTR FirstNameToReturn) | |
| { | |
| FIXME("Stub (%s %d %p %d %p %p %d %s %s)\n", | |
| debugstr_w(ServerName), Level, Bufptr, PrefMaxlen, EntriesRead, totalentries, | |
| servertype, debugstr_w(domain), debugstr_w(FirstNameToReturn)); | |
| return ERROR_NO_BROWSER_SERVERS_FOUND; | |
| } | |
| /************************************************************ | |
| * NetServerDiskEnum (NETAPI32.@) | |
| */ | |
| NET_API_STATUS WINAPI NetServerDiskEnum( | |
| LMCSTR ServerName, | |
| DWORD Level, | |
| LPBYTE *Bufptr, | |
| DWORD PrefMaxlen, | |
| LPDWORD EntriesRead, | |
| LPDWORD totalentries, | |
| LPDWORD Resume_Handle) | |
| { | |
| FIXME("Stub (%s %d %p %d %p %p %p)\n", debugstr_w(ServerName), | |
| Level, Bufptr, PrefMaxlen, EntriesRead, totalentries, Resume_Handle); | |
| return ERROR_NO_BROWSER_SERVERS_FOUND; | |
| } | |
| /************************************************************ | |
| * NetServerGetInfo (NETAPI32.@) | |
| */ | |
| NET_API_STATUS WINAPI NetServerGetInfo(LMSTR servername, DWORD level, LPBYTE* bufptr) | |
| { | |
| NET_API_STATUS ret; | |
| BOOL local = NETAPI_IsLocalComputer( servername ); | |
| TRACE("%s %d %p\n", debugstr_w( servername ), level, bufptr ); | |
| if (!local) | |
| { | |
| if (libnetapi_init()) return server_getinfo( servername, level, bufptr ); | |
| FIXME( "remote computers not supported\n" ); | |
| return ERROR_INVALID_LEVEL; | |
| } | |
| if (!bufptr) return ERROR_INVALID_PARAMETER; | |
| switch (level) | |
| { | |
| case 100: | |
| case 101: | |
| { | |
| DWORD computerNameLen, size; | |
| WCHAR computerName[MAX_COMPUTERNAME_LENGTH + 1]; | |
| computerNameLen = MAX_COMPUTERNAME_LENGTH + 1; | |
| GetComputerNameW(computerName, &computerNameLen); | |
| computerNameLen++; /* include NULL terminator */ | |
| /* Plus 1 for empty comment */ | |
| size = sizeof(SERVER_INFO_101) + (computerNameLen + 1) * sizeof(WCHAR); | |
| ret = NetApiBufferAllocate(size, (LPVOID *)bufptr); | |
| if (ret == NERR_Success) | |
| { | |
| /* INFO_100 structure is a subset of INFO_101 */ | |
| PSERVER_INFO_101 info = (PSERVER_INFO_101)*bufptr; | |
| OSVERSIONINFOW verInfo; | |
| info->sv101_platform_id = PLATFORM_ID_NT; | |
| info->sv101_name = (LMSTR)(*bufptr + sizeof(SERVER_INFO_101)); | |
| memcpy(info->sv101_name, computerName, | |
| computerNameLen * sizeof(WCHAR)); | |
| verInfo.dwOSVersionInfoSize = sizeof(verInfo); | |
| GetVersionExW(&verInfo); | |
| info->sv101_version_major = verInfo.dwMajorVersion; | |
| info->sv101_version_minor = verInfo.dwMinorVersion; | |
| /* Use generic type as no wine equivalent of DC / Server */ | |
| info->sv101_type = SV_TYPE_NT; | |
| info->sv101_comment = (LMSTR)(*bufptr + sizeof(SERVER_INFO_101) | |
| + computerNameLen * sizeof(WCHAR)); | |
| info->sv101_comment[0] = '\0'; | |
| } | |
| break; | |
| } | |
| default: | |
| FIXME("level %d unimplemented\n", level); | |
| ret = ERROR_INVALID_LEVEL; | |
| } | |
| return ret; | |
| } | |
| /************************************************************ | |
| * NetStatisticsGet (NETAPI32.@) | |
| */ | |
| NET_API_STATUS WINAPI NetStatisticsGet(LMSTR server, LMSTR service, | |
| DWORD level, DWORD options, | |
| LPBYTE *bufptr) | |
| { | |
| int res; | |
| static const WCHAR SERVICE_WORKSTATION[] = { | |
| 'L', 'a', 'n', 'm', 'a', 'n', | |
| 'W', 'o', 'r', 'k', 's', 't', 'a', 't', 'i', 'o', 'n', '\0'}; | |
| static const WCHAR SERVICE_SERVER[] = { | |
| 'L', 'a', 'n', 'm', 'a', 'n', | |
| 'S', 'e', 'r', 'v', 'e', 'r', '\0'}; | |
| union | |
| { | |
| STAT_WORKSTATION_0 workst; | |
| STAT_SERVER_0 server; | |
| } *stat; | |
| void *dataptr; | |
| TRACE("(server %s, service %s, level %d, options %d, buffer %p): stub\n", | |
| debugstr_w(server), debugstr_w(service), level, options, bufptr); | |
| res = NetApiBufferAllocate(sizeof(*stat), &dataptr); | |
| if (res != NERR_Success) return res; | |
| res = NERR_InternalError; | |
| stat = dataptr; | |
| switch (level) | |
| { | |
| case 0: | |
| if (!lstrcmpW(service, SERVICE_WORKSTATION)) | |
| { | |
| /* Fill the struct STAT_WORKSTATION_0 properly */ | |
| memset(&stat->workst, 0, sizeof(stat->workst)); | |
| res = NERR_Success; | |
| } | |
| else if (!lstrcmpW(service, SERVICE_SERVER)) | |
| { | |
| /* Fill the struct STAT_SERVER_0 properly */ | |
| memset(&stat->server, 0, sizeof(stat->server)); | |
| res = NERR_Success; | |
| } | |
| break; | |
| } | |
| if (res != NERR_Success) | |
| NetApiBufferFree(dataptr); | |
| else | |
| *bufptr = dataptr; | |
| return res; | |
| } | |
| NET_API_STATUS WINAPI NetUseEnum(LMSTR server, DWORD level, LPBYTE* bufptr, DWORD prefmaxsize, | |
| LPDWORD entriesread, LPDWORD totalentries, LPDWORD resumehandle) | |
| { | |
| FIXME("stub (%p, %d, %p, %d, %p, %p, %p)\n", server, level, bufptr, prefmaxsize, | |
| entriesread, totalentries, resumehandle); | |
| return ERROR_NOT_SUPPORTED; | |
| } | |
| NET_API_STATUS WINAPI NetScheduleJobAdd(LPCWSTR server, LPBYTE bufptr, LPDWORD jobid) | |
| { | |
| FIXME("stub (%s, %p, %p)\n", debugstr_w(server), bufptr, jobid); | |
| return NERR_Success; | |
| } | |
| NET_API_STATUS WINAPI NetScheduleJobDel(LPCWSTR server, DWORD minjobid, DWORD maxjobid) | |
| { | |
| FIXME("stub (%s, %d, %d)\n", debugstr_w(server), minjobid, maxjobid); | |
| return NERR_Success; | |
| } | |
| NET_API_STATUS WINAPI NetScheduleJobEnum(LPCWSTR server, LPBYTE* bufptr, DWORD prefmaxsize, LPDWORD entriesread, | |
| LPDWORD totalentries, LPDWORD resumehandle) | |
| { | |
| FIXME("stub (%s, %p, %d, %p, %p, %p)\n", debugstr_w(server), bufptr, prefmaxsize, entriesread, totalentries, resumehandle); | |
| *entriesread = 0; | |
| *totalentries = 0; | |
| return NERR_Success; | |
| } | |
| NET_API_STATUS WINAPI NetUseGetInfo(LMSTR server, LMSTR name, DWORD level, LPBYTE *bufptr) | |
| { | |
| FIXME("stub (%p, %p, %d, %p)\n", server, name, level, bufptr); | |
| return ERROR_NOT_SUPPORTED; | |
| } | |
| /************************************************************ | |
| * NetApiBufferAllocate (NETAPI32.@) | |
| */ | |
| NET_API_STATUS WINAPI NetApiBufferAllocate(DWORD ByteCount, LPVOID* Buffer) | |
| { | |
| TRACE("(%d, %p)\n", ByteCount, Buffer); | |
| if (Buffer == NULL) return ERROR_INVALID_PARAMETER; | |
| *Buffer = HeapAlloc(GetProcessHeap(), 0, ByteCount); | |
| if (*Buffer) | |
| return NERR_Success; | |
| else | |
| return GetLastError(); | |
| } | |
| /************************************************************ | |
| * NetApiBufferFree (NETAPI32.@) | |
| */ | |
| NET_API_STATUS WINAPI NetApiBufferFree(LPVOID Buffer) | |
| { | |
| TRACE("(%p)\n", Buffer); | |
| HeapFree(GetProcessHeap(), 0, Buffer); | |
| return NERR_Success; | |
| } | |
| /************************************************************ | |
| * NetApiBufferReallocate (NETAPI32.@) | |
| */ | |
| NET_API_STATUS WINAPI NetApiBufferReallocate(LPVOID OldBuffer, DWORD NewByteCount, | |
| LPVOID* NewBuffer) | |
| { | |
| TRACE("(%p, %d, %p)\n", OldBuffer, NewByteCount, NewBuffer); | |
| if (NewByteCount) | |
| { | |
| if (OldBuffer) | |
| *NewBuffer = HeapReAlloc(GetProcessHeap(), 0, OldBuffer, NewByteCount); | |
| else | |
| *NewBuffer = HeapAlloc(GetProcessHeap(), 0, NewByteCount); | |
| return *NewBuffer ? NERR_Success : GetLastError(); | |
| } | |
| else | |
| { | |
| if (!HeapFree(GetProcessHeap(), 0, OldBuffer)) return GetLastError(); | |
| *NewBuffer = 0; | |
| return NERR_Success; | |
| } | |
| } | |
| /************************************************************ | |
| * NetApiBufferSize (NETAPI32.@) | |
| */ | |
| NET_API_STATUS WINAPI NetApiBufferSize(LPVOID Buffer, LPDWORD ByteCount) | |
| { | |
| DWORD dw; | |
| TRACE("(%p, %p)\n", Buffer, ByteCount); | |
| if (Buffer == NULL) | |
| return ERROR_INVALID_PARAMETER; | |
| dw = HeapSize(GetProcessHeap(), 0, Buffer); | |
| TRACE("size: %d\n", dw); | |
| if (dw != 0xFFFFFFFF) | |
| *ByteCount = dw; | |
| else | |
| *ByteCount = 0; | |
| return NERR_Success; | |
| } | |
| /************************************************************ | |
| * NetSessionEnum (NETAPI32.@) | |
| * | |
| * PARAMS | |
| * servername [I] Pointer to a string with the name of the server | |
| * UncClientName [I] Pointer to a string with the name of the session | |
| * username [I] Pointer to a string with the name of the user | |
| * level [I] Data information level | |
| * bufptr [O] Buffer to the data | |
| * prefmaxlen [I] Preferred maximum length of the data | |
| * entriesread [O] Pointer to the number of entries enumerated | |
| * totalentries [O] Pointer to the possible number of entries | |
| * resume_handle [I/O] Pointer to a handle for subsequent searches | |
| * | |
| * RETURNS | |
| * If successful, the function returns NERR_Success | |
| * On failure it returns: | |
| * ERROR_ACCESS_DENIED User has no access to the requested information | |
| * ERROR_INVALID_LEVEL Value of 'level' is not correct | |
| * ERROR_INVALID_PARAMETER Wrong parameter | |
| * ERROR_MORE_DATA Need a larger buffer | |
| * ERROR_NOT_ENOUGH_MEMORY Not enough memory | |
| * NERR_ClientNameNotFound A session does not exist on a given computer | |
| * NERR_InvalidComputer Invalid computer name | |
| * NERR_UserNotFound User name could not be found. | |
| */ | |
| NET_API_STATUS WINAPI NetSessionEnum(LMSTR servername, LMSTR UncClientName, | |
| LMSTR username, DWORD level, LPBYTE* bufptr, DWORD prefmaxlen, LPDWORD entriesread, | |
| LPDWORD totalentries, LPDWORD resume_handle) | |
| { | |
| FIXME("Stub (%s %s %s %d %p %d %p %p %p)\n", debugstr_w(servername), | |
| debugstr_w(UncClientName), debugstr_w(username), | |
| level, bufptr, prefmaxlen, entriesread, totalentries, resume_handle); | |
| return NERR_Success; | |
| } | |
| /************************************************************ | |
| * NetShareEnum (NETAPI32.@) | |
| * | |
| * PARAMS | |
| * servername [I] Pointer to a string with the name of the server | |
| * level [I] Data information level | |
| * bufptr [O] Buffer to the data | |
| * prefmaxlen [I] Preferred maximum length of the data | |
| * entriesread [O] Pointer to the number of entries enumerated | |
| * totalentries [O] Pointer to the possible number of entries | |
| * resume_handle [I/O] Pointer to a handle for subsequent searches | |
| * | |
| * RETURNS | |
| * If successful, the function returns NERR_Success | |
| * On failure it returns a system error code (FIXME: find out which) | |
| * | |
| */ | |
| NET_API_STATUS WINAPI NetShareEnum( LMSTR servername, DWORD level, LPBYTE* bufptr, | |
| DWORD prefmaxlen, LPDWORD entriesread, LPDWORD totalentries, LPDWORD resume_handle) | |
| { | |
| FIXME("Stub (%s %d %p %d %p %p %p)\n", debugstr_w(servername), level, bufptr, | |
| prefmaxlen, entriesread, totalentries, resume_handle); | |
| return ERROR_NOT_SUPPORTED; | |
| } | |
| /************************************************************ | |
| * NetShareDel (NETAPI32.@) | |
| */ | |
| NET_API_STATUS WINAPI NetShareDel(LMSTR servername, LMSTR netname, DWORD reserved) | |
| { | |
| BOOL local = NETAPI_IsLocalComputer( servername ); | |
| TRACE("%s %s %d\n", debugstr_w(servername), debugstr_w(netname), reserved); | |
| if (!local) | |
| { | |
| if (libnetapi_init()) return share_del( servername, netname, reserved ); | |
| FIXME( "remote computers not supported\n" ); | |
| } | |
| FIXME("%s %s %d\n", debugstr_w(servername), debugstr_w(netname), reserved); | |
| return NERR_Success; | |
| } | |
| /************************************************************ | |
| * NetShareGetInfo (NETAPI32.@) | |
| */ | |
| NET_API_STATUS WINAPI NetShareGetInfo(LMSTR servername, LMSTR netname, | |
| DWORD level, LPBYTE *bufptr) | |
| { | |
| FIXME("Stub (%s %s %d %p)\n", debugstr_w(servername), | |
| debugstr_w(netname),level, bufptr); | |
| return NERR_NetNameNotFound; | |
| } | |
| /************************************************************ | |
| * NetShareAdd (NETAPI32.@) | |
| */ | |
| NET_API_STATUS WINAPI NetShareAdd(LMSTR servername, | |
| DWORD level, LPBYTE buf, LPDWORD parm_err) | |
| { | |
| BOOL local = NETAPI_IsLocalComputer( servername ); | |
| TRACE("%s %d %p %p\n", debugstr_w(servername), level, buf, parm_err); | |
| if (!local) | |
| { | |
| if (libnetapi_init()) return share_add( servername, level, buf, parm_err ); | |
| FIXME( "remote computers not supported\n" ); | |
| } | |
| FIXME("%s %d %p %p\n", debugstr_w(servername), level, buf, parm_err); | |
| return ERROR_NOT_SUPPORTED; | |
| } | |
| /************************************************************ | |
| * NetFileEnum (NETAPI32.@) | |
| */ | |
| NET_API_STATUS WINAPI NetFileEnum( | |
| LPWSTR ServerName, LPWSTR BasePath, LPWSTR UserName, | |
| DWORD Level, LPBYTE* BufPtr, DWORD PrefMaxLen, | |
| LPDWORD EntriesRead, LPDWORD TotalEntries, PDWORD_PTR ResumeHandle) | |
| { | |
| FIXME("(%s, %s, %s, %u): stub\n", debugstr_w(ServerName), debugstr_w(BasePath), | |
| debugstr_w(UserName), Level); | |
| return ERROR_NOT_SUPPORTED; | |
| } | |
| static void wprint_mac(WCHAR* buffer, int len, const MIB_IFROW *ifRow) | |
| { | |
| int i; | |
| unsigned char val; | |
| if (!buffer) | |
| return; | |
| if (len < 1) | |
| return; | |
| if (!ifRow) | |
| { | |
| *buffer = '\0'; | |
| return; | |
| } | |
| for (i = 0; i < ifRow->dwPhysAddrLen && 2 * i < len; i++) | |
| { | |
| val = ifRow->bPhysAddr[i]; | |
| if ((val >>4) >9) | |
| buffer[2*i] = (WCHAR)((val >>4) + 'A' - 10); | |
| else | |
| buffer[2*i] = (WCHAR)((val >>4) + '0'); | |
| if ((val & 0xf ) >9) | |
| buffer[2*i+1] = (WCHAR)((val & 0xf) + 'A' - 10); | |
| else | |
| buffer[2*i+1] = (WCHAR)((val & 0xf) + '0'); | |
| } | |
| buffer[2*i]=0; | |
| } | |
| /* Theoretically this could be too short, except that MS defines | |
| * MAX_ADAPTER_NAME as 128, and MAX_INTERFACE_NAME_LEN as 256, and both | |
| * represent a count of WCHARs, so even with an extraordinarily long header | |
| * this will be plenty | |
| */ | |
| #define MAX_TRANSPORT_NAME MAX_INTERFACE_NAME_LEN | |
| #define MAX_TRANSPORT_ADDR 13 | |
| #define NBT_TRANSPORT_NAME_HEADER "\\Device\\NetBT_Tcpip_" | |
| #define UNKNOWN_TRANSPORT_NAME_HEADER "\\Device\\UnknownTransport_" | |
| static void wprint_name(WCHAR *buffer, int len, ULONG transport, | |
| PMIB_IFROW ifRow) | |
| { | |
| WCHAR *ptr1, *ptr2; | |
| const char *name; | |
| if (!buffer) | |
| return; | |
| if (!ifRow) | |
| { | |
| *buffer = '\0'; | |
| return; | |
| } | |
| if (!memcmp(&transport, TRANSPORT_NBT, sizeof(ULONG))) | |
| name = NBT_TRANSPORT_NAME_HEADER; | |
| else | |
| name = UNKNOWN_TRANSPORT_NAME_HEADER; | |
| for (ptr1 = buffer; *name && ptr1 < buffer + len; ptr1++, name++) | |
| *ptr1 = *name; | |
| for (ptr2 = ifRow->wszName; *ptr2 && ptr1 < buffer + len; ptr1++, ptr2++) | |
| *ptr1 = *ptr2; | |
| *ptr1 = '\0'; | |
| } | |
| /*********************************************************************** | |
| * NetWkstaTransportEnum (NETAPI32.@) | |
| */ | |
| struct WkstaTransportEnumData | |
| { | |
| UCHAR n_adapt; | |
| UCHAR n_read; | |
| DWORD prefmaxlen; | |
| LPBYTE *pbuf; | |
| NET_API_STATUS ret; | |
| }; | |
| /**********************************************************************/ | |
| static BOOL WkstaEnumAdaptersCallback(UCHAR totalLANAs, UCHAR lanaIndex, | |
| ULONG transport, const NetBIOSAdapterImpl *data, void *closure) | |
| { | |
| BOOL ret; | |
| struct WkstaTransportEnumData *enumData = closure; | |
| if (enumData && enumData->pbuf) | |
| { | |
| if (lanaIndex == 0) | |
| { | |
| DWORD toAllocate; | |
| enumData->n_adapt = totalLANAs; | |
| enumData->n_read = 0; | |
| toAllocate = totalLANAs * (sizeof(WKSTA_TRANSPORT_INFO_0) | |
| + MAX_TRANSPORT_NAME * sizeof(WCHAR) + | |
| MAX_TRANSPORT_ADDR * sizeof(WCHAR)); | |
| if (enumData->prefmaxlen != MAX_PREFERRED_LENGTH) | |
| toAllocate = enumData->prefmaxlen; | |
| NetApiBufferAllocate(toAllocate, (LPVOID *)enumData->pbuf); | |
| } | |
| if (*(enumData->pbuf)) | |
| { | |
| UCHAR spaceFor; | |
| if (enumData->prefmaxlen == MAX_PREFERRED_LENGTH) | |
| spaceFor = totalLANAs; | |
| else | |
| spaceFor = enumData->prefmaxlen / | |
| (sizeof(WKSTA_TRANSPORT_INFO_0) + (MAX_TRANSPORT_NAME + | |
| MAX_TRANSPORT_ADDR) * sizeof(WCHAR)); | |
| if (enumData->n_read < spaceFor) | |
| { | |
| PWKSTA_TRANSPORT_INFO_0 ti; | |
| LMSTR transport_name, transport_addr; | |
| MIB_IFROW ifRow; | |
| ti = (PWKSTA_TRANSPORT_INFO_0)(*(enumData->pbuf) + | |
| enumData->n_read * sizeof(WKSTA_TRANSPORT_INFO_0)); | |
| transport_name = (LMSTR)(*(enumData->pbuf) + | |
| totalLANAs * sizeof(WKSTA_TRANSPORT_INFO_0) + | |
| enumData->n_read * MAX_TRANSPORT_NAME * sizeof(WCHAR)); | |
| transport_addr = (LMSTR)(*(enumData->pbuf) + | |
| totalLANAs * (sizeof(WKSTA_TRANSPORT_INFO_0) + | |
| MAX_TRANSPORT_NAME * sizeof(WCHAR)) + | |
| enumData->n_read * MAX_TRANSPORT_ADDR * sizeof(WCHAR)); | |
| ifRow.dwIndex = data->ifIndex; | |
| GetIfEntry(&ifRow); | |
| ti->wkti0_quality_of_service = 0; | |
| ti->wkti0_number_of_vcs = 0; | |
| ti->wkti0_transport_name = transport_name; | |
| wprint_name(ti->wkti0_transport_name, MAX_TRANSPORT_NAME, | |
| transport, &ifRow); | |
| ti->wkti0_transport_address = transport_addr; | |
| wprint_mac(ti->wkti0_transport_address, MAX_TRANSPORT_ADDR, | |
| &ifRow); | |
| if (!memcmp(&transport, TRANSPORT_NBT, sizeof(ULONG))) | |
| ti->wkti0_wan_ish = TRUE; | |
| else | |
| ti->wkti0_wan_ish = FALSE; | |
| TRACE("%d of %d:ti at %p\n", lanaIndex, totalLANAs, ti); | |
| TRACE("transport_name at %p %s\n", | |
| ti->wkti0_transport_name, | |
| debugstr_w(ti->wkti0_transport_name)); | |
| TRACE("transport_address at %p %s\n", | |
| ti->wkti0_transport_address, | |
| debugstr_w(ti->wkti0_transport_address)); | |
| enumData->n_read++; | |
| enumData->ret = NERR_Success; | |
| ret = TRUE; | |
| } | |
| else | |
| { | |
| enumData->ret = ERROR_MORE_DATA; | |
| ret = FALSE; | |
| } | |
| } | |
| else | |
| { | |
| enumData->ret = ERROR_OUTOFMEMORY; | |
| ret = FALSE; | |
| } | |
| } | |
| else | |
| ret = FALSE; | |
| return ret; | |
| } | |
| /**********************************************************************/ | |
| NET_API_STATUS WINAPI | |
| NetWkstaTransportEnum(LMSTR ServerName, DWORD level, PBYTE* pbuf, | |
| DWORD prefmaxlen, LPDWORD read_entries, | |
| PDWORD total_entries, PDWORD hresume) | |
| { | |
| NET_API_STATUS ret; | |
| TRACE(":%s, 0x%08x, %p, 0x%08x, %p, %p, %p\n", debugstr_w(ServerName), | |
| level, pbuf, prefmaxlen, read_entries, total_entries,hresume); | |
| if (!NETAPI_IsLocalComputer(ServerName)) | |
| { | |
| FIXME(":not implemented for non-local computers\n"); | |
| ret = ERROR_INVALID_LEVEL; | |
| } | |
| else | |
| { | |
| if (hresume && *hresume) | |
| { | |
| FIXME(":resume handle not implemented\n"); | |
| return ERROR_INVALID_LEVEL; | |
| } | |
| switch (level) | |
| { | |
| case 0: /* transport info */ | |
| { | |
| ULONG allTransports; | |
| struct WkstaTransportEnumData enumData; | |
| if (NetBIOSNumAdapters() == 0) | |
| return ERROR_NETWORK_UNREACHABLE; | |
| if (!read_entries) | |
| return STATUS_ACCESS_VIOLATION; | |
| if (!total_entries || !pbuf) | |
| return RPC_X_NULL_REF_POINTER; | |
| enumData.prefmaxlen = prefmaxlen; | |
| enumData.pbuf = pbuf; | |
| memcpy(&allTransports, ALL_TRANSPORTS, sizeof(ULONG)); | |
| NetBIOSEnumAdapters(allTransports, WkstaEnumAdaptersCallback, | |
| &enumData); | |
| *read_entries = enumData.n_read; | |
| *total_entries = enumData.n_adapt; | |
| if (hresume) *hresume= 0; | |
| ret = enumData.ret; | |
| break; | |
| } | |
| default: | |
| TRACE("Invalid level %d is specified\n", level); | |
| ret = ERROR_INVALID_LEVEL; | |
| } | |
| } | |
| return ret; | |
| } | |
| /************************************************************ | |
| * NetWkstaUserGetInfo (NETAPI32.@) | |
| */ | |
| NET_API_STATUS WINAPI NetWkstaUserGetInfo(LMSTR reserved, DWORD level, | |
| PBYTE* bufptr) | |
| { | |
| NET_API_STATUS nastatus; | |
| TRACE("(%s, %d, %p)\n", debugstr_w(reserved), level, bufptr); | |
| switch (level) | |
| { | |
| case 0: | |
| { | |
| PWKSTA_USER_INFO_0 ui; | |
| DWORD dwSize = UNLEN + 1; | |
| /* set up buffer */ | |
| nastatus = NetApiBufferAllocate(sizeof(WKSTA_USER_INFO_0) + dwSize * sizeof(WCHAR), | |
| (LPVOID *) bufptr); | |
| if (nastatus != NERR_Success) | |
| return ERROR_NOT_ENOUGH_MEMORY; | |
| ui = (PWKSTA_USER_INFO_0) *bufptr; | |
| ui->wkui0_username = (LMSTR) (*bufptr + sizeof(WKSTA_USER_INFO_0)); | |
| /* get data */ | |
| if (!GetUserNameW(ui->wkui0_username, &dwSize)) | |
| { | |
| NetApiBufferFree(ui); | |
| return ERROR_NOT_ENOUGH_MEMORY; | |
| } | |
| else { | |
| nastatus = NetApiBufferReallocate( | |
| *bufptr, sizeof(WKSTA_USER_INFO_0) + | |
| (lstrlenW(ui->wkui0_username) + 1) * sizeof(WCHAR), | |
| (LPVOID *) bufptr); | |
| if (nastatus != NERR_Success) | |
| { | |
| NetApiBufferFree(ui); | |
| return nastatus; | |
| } | |
| ui = (PWKSTA_USER_INFO_0) *bufptr; | |
| ui->wkui0_username = (LMSTR) (*bufptr + sizeof(WKSTA_USER_INFO_0)); | |
| } | |
| break; | |
| } | |
| case 1: | |
| { | |
| PWKSTA_USER_INFO_1 ui; | |
| PWKSTA_USER_INFO_0 ui0; | |
| LSA_OBJECT_ATTRIBUTES ObjectAttributes; | |
| LSA_HANDLE PolicyHandle; | |
| PPOLICY_ACCOUNT_DOMAIN_INFO DomainInfo; | |
| NTSTATUS NtStatus; | |
| /* sizes of the field buffers in WCHARS */ | |
| int username_sz, logon_domain_sz, oth_domains_sz, logon_server_sz; | |
| FIXME("Level 1 processing is partially implemented\n"); | |
| oth_domains_sz = 1; | |
| logon_server_sz = 1; | |
| /* get some information first to estimate size of the buffer */ | |
| ui0 = NULL; | |
| nastatus = NetWkstaUserGetInfo(NULL, 0, (PBYTE *) &ui0); | |
| if (nastatus != NERR_Success) | |
| return nastatus; | |
| username_sz = lstrlenW(ui0->wkui0_username) + 1; | |
| ZeroMemory(&ObjectAttributes, sizeof(ObjectAttributes)); | |
| NtStatus = LsaOpenPolicy(NULL, &ObjectAttributes, | |
| POLICY_VIEW_LOCAL_INFORMATION, | |
| &PolicyHandle); | |
| if (NtStatus != STATUS_SUCCESS) | |
| { | |
| TRACE("LsaOpenPolicyFailed with NT status %x\n", | |
| LsaNtStatusToWinError(NtStatus)); | |
| NetApiBufferFree(ui0); | |
| return ERROR_NOT_ENOUGH_MEMORY; | |
| } | |
| LsaQueryInformationPolicy(PolicyHandle, PolicyAccountDomainInformation, | |
| (PVOID*) &DomainInfo); | |
| logon_domain_sz = lstrlenW(DomainInfo->DomainName.Buffer) + 1; | |
| LsaClose(PolicyHandle); | |
| /* set up buffer */ | |
| nastatus = NetApiBufferAllocate(sizeof(WKSTA_USER_INFO_1) + | |
| (username_sz + logon_domain_sz + | |
| oth_domains_sz + logon_server_sz) * sizeof(WCHAR), | |
| (LPVOID *) bufptr); | |
| if (nastatus != NERR_Success) { | |
| NetApiBufferFree(ui0); | |
| return nastatus; | |
| } | |
| ui = (WKSTA_USER_INFO_1 *) *bufptr; | |
| ui->wkui1_username = (LMSTR) (*bufptr + sizeof(WKSTA_USER_INFO_1)); | |
| ui->wkui1_logon_domain = (LMSTR) ( | |
| ((PBYTE) ui->wkui1_username) + username_sz * sizeof(WCHAR)); | |
| ui->wkui1_oth_domains = (LMSTR) ( | |
| ((PBYTE) ui->wkui1_logon_domain) + | |
| logon_domain_sz * sizeof(WCHAR)); | |
| ui->wkui1_logon_server = (LMSTR) ( | |
| ((PBYTE) ui->wkui1_oth_domains) + | |
| oth_domains_sz * sizeof(WCHAR)); | |
| /* get data */ | |
| lstrcpyW(ui->wkui1_username, ui0->wkui0_username); | |
| NetApiBufferFree(ui0); | |
| lstrcpynW(ui->wkui1_logon_domain, DomainInfo->DomainName.Buffer, | |
| logon_domain_sz); | |
| LsaFreeMemory(DomainInfo); | |
| /* FIXME. Not implemented. Populated with empty strings */ | |
| ui->wkui1_oth_domains[0] = 0; | |
| ui->wkui1_logon_server[0] = 0; | |
| break; | |
| } | |
| case 1101: | |
| { | |
| PWKSTA_USER_INFO_1101 ui; | |
| DWORD dwSize = 1; | |
| FIXME("Stub. Level 1101 processing is not implemented\n"); | |
| /* FIXME see also wkui1_oth_domains for level 1 */ | |
| /* set up buffer */ | |
| nastatus = NetApiBufferAllocate(sizeof(WKSTA_USER_INFO_1101) + dwSize * sizeof(WCHAR), | |
| (LPVOID *) bufptr); | |
| if (nastatus != NERR_Success) | |
| return nastatus; | |
| ui = (PWKSTA_USER_INFO_1101) *bufptr; | |
| ui->wkui1101_oth_domains = (LMSTR)(ui + 1); | |
| /* get data */ | |
| ui->wkui1101_oth_domains[0] = 0; | |
| break; | |
| } | |
| default: | |
| TRACE("Invalid level %d is specified\n", level); | |
| return ERROR_INVALID_LEVEL; | |
| } | |
| return NERR_Success; | |
| } | |
| /************************************************************ | |
| * NetWkstaUserEnum (NETAPI32.@) | |
| */ | |
| NET_API_STATUS WINAPI | |
| NetWkstaUserEnum(LMSTR servername, DWORD level, LPBYTE* bufptr, | |
| DWORD prefmaxlen, LPDWORD entriesread, | |
| LPDWORD totalentries, LPDWORD resumehandle) | |
| { | |
| FIXME("(%s, %d, %p, %d, %p, %p, %p): stub!\n", debugstr_w(servername), | |
| level, bufptr, prefmaxlen, entriesread, totalentries, resumehandle); | |
| return ERROR_INVALID_PARAMETER; | |
| } | |
| /************************************************************ | |
| * NetpGetComputerName (NETAPI32.@) | |
| */ | |
| NET_API_STATUS WINAPI NetpGetComputerName(LPWSTR *Buffer) | |
| { | |
| DWORD dwSize = MAX_COMPUTERNAME_LENGTH + 1; | |
| TRACE("(%p)\n", Buffer); | |
| NetApiBufferAllocate(dwSize * sizeof(WCHAR), (LPVOID *) Buffer); | |
| if (GetComputerNameW(*Buffer, &dwSize)) | |
| { | |
| return NetApiBufferReallocate( | |
| *Buffer, (dwSize + 1) * sizeof(WCHAR), | |
| (LPVOID *) Buffer); | |
| } | |
| else | |
| { | |
| NetApiBufferFree(*Buffer); | |
| return ERROR_NOT_ENOUGH_MEMORY; | |
| } | |
| } | |
| NET_API_STATUS WINAPI I_NetNameCompare(LPVOID p1, LPWSTR wkgrp, LPWSTR comp, | |
| LPVOID p4, LPVOID p5) | |
| { | |
| FIXME("(%p %s %s %p %p): stub\n", p1, debugstr_w(wkgrp), debugstr_w(comp), | |
| p4, p5); | |
| return ERROR_INVALID_PARAMETER; | |
| } | |
| NET_API_STATUS WINAPI I_NetNameValidate(LPVOID p1, LPWSTR wkgrp, LPVOID p3, | |
| LPVOID p4) | |
| { | |
| FIXME("(%p %s %p %p): stub\n", p1, debugstr_w(wkgrp), p3, p4); | |
| return ERROR_INVALID_PARAMETER; | |
| } | |
| NET_API_STATUS WINAPI NetWkstaGetInfo( LMSTR servername, DWORD level, | |
| LPBYTE* bufptr) | |
| { | |
| NET_API_STATUS ret; | |
| BOOL local = NETAPI_IsLocalComputer( servername ); | |
| TRACE("%s %d %p\n", debugstr_w( servername ), level, bufptr ); | |
| if (!local) | |
| { | |
| if (libnetapi_init()) return wksta_getinfo( servername, level, bufptr ); | |
| FIXME( "remote computers not supported\n" ); | |
| return ERROR_INVALID_LEVEL; | |
| } | |
| if (!bufptr) return ERROR_INVALID_PARAMETER; | |
| switch (level) | |
| { | |
| case 100: | |
| case 101: | |
| case 102: | |
| { | |
| static const WCHAR lanroot[] = {'c',':','\\','l','a','n','m','a','n',0}; /* FIXME */ | |
| DWORD computerNameLen, domainNameLen, size; | |
| WCHAR computerName[MAX_COMPUTERNAME_LENGTH + 1]; | |
| LSA_OBJECT_ATTRIBUTES ObjectAttributes; | |
| LSA_HANDLE PolicyHandle; | |
| NTSTATUS NtStatus; | |
| computerNameLen = MAX_COMPUTERNAME_LENGTH + 1; | |
| GetComputerNameW(computerName, &computerNameLen); | |
| computerNameLen++; /* include NULL terminator */ | |
| ZeroMemory(&ObjectAttributes, sizeof(ObjectAttributes)); | |
| NtStatus = LsaOpenPolicy(NULL, &ObjectAttributes, | |
| POLICY_VIEW_LOCAL_INFORMATION, &PolicyHandle); | |
| if (NtStatus != STATUS_SUCCESS) | |
| ret = LsaNtStatusToWinError(NtStatus); | |
| else | |
| { | |
| PPOLICY_ACCOUNT_DOMAIN_INFO DomainInfo; | |
| LsaQueryInformationPolicy(PolicyHandle, | |
| PolicyAccountDomainInformation, (PVOID*)&DomainInfo); | |
| domainNameLen = lstrlenW(DomainInfo->DomainName.Buffer) + 1; | |
| size = sizeof(WKSTA_INFO_102) + computerNameLen * sizeof(WCHAR) | |
| + domainNameLen * sizeof(WCHAR) + sizeof(lanroot); | |
| ret = NetApiBufferAllocate(size, (LPVOID *)bufptr); | |
| if (ret == NERR_Success) | |
| { | |
| /* INFO_100 and INFO_101 structures are subsets of INFO_102 */ | |
| PWKSTA_INFO_102 info = (PWKSTA_INFO_102)*bufptr; | |
| OSVERSIONINFOW verInfo; | |
| info->wki102_platform_id = PLATFORM_ID_NT; | |
| info->wki102_computername = (LMSTR)(*bufptr + | |
| sizeof(WKSTA_INFO_102)); | |
| memcpy(info->wki102_computername, computerName, | |
| computerNameLen * sizeof(WCHAR)); | |
| info->wki102_langroup = info->wki102_computername + computerNameLen; | |
| memcpy(info->wki102_langroup, DomainInfo->DomainName.Buffer, | |
| domainNameLen * sizeof(WCHAR)); | |
| info->wki102_lanroot = info->wki102_langroup + domainNameLen; | |
| memcpy(info->wki102_lanroot, lanroot, sizeof(lanroot)); | |
| memset(&verInfo, 0, sizeof(verInfo)); | |
| verInfo.dwOSVersionInfoSize = sizeof(verInfo); | |
| GetVersionExW(&verInfo); | |
| info->wki102_ver_major = verInfo.dwMajorVersion; | |
| info->wki102_ver_minor = verInfo.dwMinorVersion; | |
| info->wki102_logged_on_users = 1; | |
| } | |
| LsaFreeMemory(DomainInfo); | |
| LsaClose(PolicyHandle); | |
| } | |
| break; | |
| } | |
| default: | |
| FIXME("level %d unimplemented\n", level); | |
| ret = ERROR_INVALID_LEVEL; | |
| } | |
| return ret; | |
| } | |
| /************************************************************ | |
| * NetGetJoinInformation (NETAPI32.@) | |
| */ | |
| NET_API_STATUS NET_API_FUNCTION NetGetJoinInformation( | |
| LPCWSTR Server, | |
| LPWSTR *Name, | |
| PNETSETUP_JOIN_STATUS type) | |
| { | |
| static const WCHAR workgroupW[] = {'W','o','r','k','g','r','o','u','p',0}; | |
| FIXME("Semi-stub %s %p %p\n", wine_dbgstr_w(Server), Name, type); | |
| if (!Name || !type) | |
| return ERROR_INVALID_PARAMETER; | |
| NetApiBufferAllocate(sizeof(workgroupW), (LPVOID *)Name); | |
| lstrcpyW(*Name, workgroupW); | |
| *type = NetSetupWorkgroupName; | |
| return NERR_Success; | |
| } | |
| /************************************************************ | |
| * NetUserGetGroups (NETAPI32.@) | |
| */ | |
| NET_API_STATUS NET_API_FUNCTION NetUserGetGroups( | |
| LPCWSTR servername, | |
| LPCWSTR username, | |
| DWORD level, | |
| LPBYTE *bufptr, | |
| DWORD prefixmaxlen, | |
| LPDWORD entriesread, | |
| LPDWORD totalentries) | |
| { | |
| FIXME("%s %s %d %p %d %p %p stub\n", debugstr_w(servername), | |
| debugstr_w(username), level, bufptr, prefixmaxlen, entriesread, | |
| totalentries); | |
| *bufptr = NULL; | |
| *entriesread = 0; | |
| *totalentries = 0; | |
| return ERROR_INVALID_LEVEL; | |
| } | |
| struct sam_user | |
| { | |
| struct list entry; | |
| WCHAR user_name[LM20_UNLEN+1]; | |
| WCHAR user_password[PWLEN + 1]; | |
| DWORD sec_since_passwd_change; | |
| DWORD user_priv; | |
| LPWSTR home_dir; | |
| LPWSTR user_comment; | |
| DWORD user_flags; | |
| LPWSTR user_logon_script_path; | |
| }; | |
| static struct list user_list = LIST_INIT( user_list ); | |
| /************************************************************ | |
| * NETAPI_ValidateServername | |
| * | |
| * Validates server name | |
| */ | |
| static NET_API_STATUS NETAPI_ValidateServername(LPCWSTR ServerName) | |
| { | |
| if (ServerName) | |
| { | |
| if (ServerName[0] == 0) | |
| return ERROR_BAD_NETPATH; | |
| else if ( | |
| ((ServerName[0] == '\\') && | |
| (ServerName[1] != '\\')) | |
| || | |
| ((ServerName[0] == '\\') && | |
| (ServerName[1] == '\\') && | |
| (ServerName[2] == 0)) | |
| ) | |
| return ERROR_INVALID_NAME; | |
| } | |
| return NERR_Success; | |
| } | |
| /************************************************************ | |
| * NETAPI_FindUser | |
| * | |
| * Looks for a user in the user database. | |
| * Returns a pointer to the entry in the user list when the user | |
| * is found, NULL otherwise. | |
| */ | |
| static struct sam_user* NETAPI_FindUser(LPCWSTR UserName) | |
| { | |
| struct sam_user *user; | |
| LIST_FOR_EACH_ENTRY(user, &user_list, struct sam_user, entry) | |
| { | |
| if(lstrcmpW(user->user_name, UserName) == 0) | |
| return user; | |
| } | |
| return NULL; | |
| } | |
| static BOOL NETAPI_IsCurrentUser(LPCWSTR username) | |
| { | |
| LPWSTR curr_user = NULL; | |
| DWORD dwSize; | |
| BOOL ret = FALSE; | |
| dwSize = LM20_UNLEN+1; | |
| curr_user = HeapAlloc(GetProcessHeap(), 0, dwSize * sizeof(WCHAR)); | |
| if(!curr_user) | |
| { | |
| ERR("Failed to allocate memory for user name.\n"); | |
| goto end; | |
| } | |
| if(!GetUserNameW(curr_user, &dwSize)) | |
| { | |
| ERR("Failed to get current user's user name.\n"); | |
| goto end; | |
| } | |
| if (!lstrcmpW(curr_user, username)) | |
| { | |
| ret = TRUE; | |
| } | |
| end: | |
| HeapFree(GetProcessHeap(), 0, curr_user); | |
| return ret; | |
| } | |
| /************************************************************ | |
| * NetUserAdd (NETAPI32.@) | |
| */ | |
| NET_API_STATUS WINAPI NetUserAdd(LPCWSTR servername, | |
| DWORD level, LPBYTE bufptr, LPDWORD parm_err) | |
| { | |
| NET_API_STATUS status; | |
| struct sam_user * su = NULL; | |
| FIXME("(%s, %d, %p, %p) stub!\n", debugstr_w(servername), level, bufptr, parm_err); | |
| if((status = NETAPI_ValidateServername(servername)) != NERR_Success) | |
| return status; | |
| switch(level) | |
| { | |
| /* Level 3 and 4 are identical for the purposes of NetUserAdd */ | |
| case 4: | |
| case 3: | |
| FIXME("Level 3 and 4 not implemented.\n"); | |
| /* Fall through */ | |
| case 2: | |
| FIXME("Level 2 not implemented.\n"); | |
| /* Fall through */ | |
| case 1: | |
| { | |
| PUSER_INFO_1 ui = (PUSER_INFO_1) bufptr; | |
| su = HeapAlloc(GetProcessHeap(), 0, sizeof(struct sam_user)); | |
| if(!su) | |
| { | |
| status = NERR_InternalError; | |
| break; | |
| } | |
| if(lstrlenW(ui->usri1_name) > LM20_UNLEN) | |
| { | |
| status = NERR_BadUsername; | |
| break; | |
| } | |
| /*FIXME: do other checks for a valid username */ | |
| lstrcpyW(su->user_name, ui->usri1_name); | |
| if(lstrlenW(ui->usri1_password) > PWLEN) | |
| { | |
| /* Always return PasswordTooShort on invalid passwords. */ | |
| status = NERR_PasswordTooShort; | |
| break; | |
| } | |
| lstrcpyW(su->user_password, ui->usri1_password); | |
| su->sec_since_passwd_change = ui->usri1_password_age; | |
| su->user_priv = ui->usri1_priv; | |
| su->user_flags = ui->usri1_flags; | |
| /*FIXME: set the other LPWSTRs to NULL for now */ | |
| su->home_dir = NULL; | |
| su->user_comment = NULL; | |
| su->user_logon_script_path = NULL; | |
| list_add_head(&user_list, &su->entry); | |
| return NERR_Success; | |
| } | |
| default: | |
| TRACE("Invalid level %d specified.\n", level); | |
| status = ERROR_INVALID_LEVEL; | |
| break; | |
| } | |
| HeapFree(GetProcessHeap(), 0, su); | |
| return status; | |
| } | |
| /************************************************************ | |
| * NetUserDel (NETAPI32.@) | |
| */ | |
| NET_API_STATUS WINAPI NetUserDel(LPCWSTR servername, LPCWSTR username) | |
| { | |
| NET_API_STATUS status; | |
| struct sam_user *user; | |
| TRACE("(%s, %s)\n", debugstr_w(servername), debugstr_w(username)); | |
| if((status = NETAPI_ValidateServername(servername))!= NERR_Success) | |
| return status; | |
| if ((user = NETAPI_FindUser(username)) == NULL) | |
| return NERR_UserNotFound; | |
| list_remove(&user->entry); | |
| HeapFree(GetProcessHeap(), 0, user->home_dir); | |
| HeapFree(GetProcessHeap(), 0, user->user_comment); | |
| HeapFree(GetProcessHeap(), 0, user->user_logon_script_path); | |
| HeapFree(GetProcessHeap(), 0, user); | |
| return NERR_Success; | |
| } | |
| /************************************************************ | |
| * NetUserGetInfo (NETAPI32.@) | |
| */ | |
| NET_API_STATUS WINAPI | |
| NetUserGetInfo(LPCWSTR servername, LPCWSTR username, DWORD level, | |
| LPBYTE* bufptr) | |
| { | |
| NET_API_STATUS status; | |
| TRACE("(%s, %s, %d, %p)\n", debugstr_w(servername), debugstr_w(username), | |
| level, bufptr); | |
| status = NETAPI_ValidateServername(servername); | |
| if (status != NERR_Success) | |
| return status; | |
| if(!NETAPI_IsLocalComputer(servername)) | |
| { | |
| FIXME("Only implemented for local computer, but remote server" | |
| "%s was requested.\n", debugstr_w(servername)); | |
| return NERR_InvalidComputer; | |
| } | |
| if(!NETAPI_FindUser(username) && !NETAPI_IsCurrentUser(username)) | |
| { | |
| TRACE("User %s is unknown.\n", debugstr_w(username)); | |
| return NERR_UserNotFound; | |
| } | |
| switch (level) | |
| { | |
| case 0: | |
| { | |
| PUSER_INFO_0 ui; | |
| int name_sz; | |
| name_sz = lstrlenW(username) + 1; | |
| /* set up buffer */ | |
| NetApiBufferAllocate(sizeof(USER_INFO_0) + name_sz * sizeof(WCHAR), | |
| (LPVOID *) bufptr); | |
| ui = (PUSER_INFO_0) *bufptr; | |
| ui->usri0_name = (LPWSTR) (*bufptr + sizeof(USER_INFO_0)); | |
| /* get data */ | |
| lstrcpyW(ui->usri0_name, username); | |
| break; | |
| } | |
| case 10: | |
| { | |
| PUSER_INFO_10 ui; | |
| PUSER_INFO_0 ui0; | |
| /* sizes of the field buffers in WCHARS */ | |
| int name_sz, comment_sz, usr_comment_sz, full_name_sz; | |
| comment_sz = 1; | |
| usr_comment_sz = 1; | |
| full_name_sz = 1; | |
| /* get data */ | |
| status = NetUserGetInfo(servername, username, 0, (LPBYTE *) &ui0); | |
| if (status != NERR_Success) | |
| { | |
| NetApiBufferFree(ui0); | |
| return status; | |
| } | |
| name_sz = lstrlenW(ui0->usri0_name) + 1; | |
| /* set up buffer */ | |
| NetApiBufferAllocate(sizeof(USER_INFO_10) + | |
| (name_sz + comment_sz + usr_comment_sz + | |
| full_name_sz) * sizeof(WCHAR), | |
| (LPVOID *) bufptr); | |
| ui = (PUSER_INFO_10) *bufptr; | |
| ui->usri10_name = (LPWSTR) (*bufptr + sizeof(USER_INFO_10)); | |
| ui->usri10_comment = (LPWSTR) ( | |
| ((PBYTE) ui->usri10_name) + name_sz * sizeof(WCHAR)); | |
| ui->usri10_usr_comment = (LPWSTR) ( | |
| ((PBYTE) ui->usri10_comment) + comment_sz * sizeof(WCHAR)); | |
| ui->usri10_full_name = (LPWSTR) ( | |
| ((PBYTE) ui->usri10_usr_comment) + usr_comment_sz * sizeof(WCHAR)); | |
| /* set data */ | |
| lstrcpyW(ui->usri10_name, ui0->usri0_name); | |
| NetApiBufferFree(ui0); | |
| ui->usri10_comment[0] = 0; | |
| ui->usri10_usr_comment[0] = 0; | |
| ui->usri10_full_name[0] = 0; | |
| break; | |
| } | |
| case 1: | |
| { | |
| static const WCHAR homedirW[] = {'H','O','M','E',0}; | |
| PUSER_INFO_1 ui; | |
| PUSER_INFO_0 ui0; | |
| /* sizes of the field buffers in WCHARS */ | |
| int name_sz, password_sz, home_dir_sz, comment_sz, script_path_sz; | |
| password_sz = 1; /* not filled out for security reasons for NetUserGetInfo*/ | |
| comment_sz = 1; | |
| script_path_sz = 1; | |
| /* get data */ | |
| status = NetUserGetInfo(servername, username, 0, (LPBYTE *) &ui0); | |
| if (status != NERR_Success) | |
| { | |
| NetApiBufferFree(ui0); | |
| return status; | |
| } | |
| name_sz = lstrlenW(ui0->usri0_name) + 1; | |
| home_dir_sz = GetEnvironmentVariableW(homedirW, NULL,0); | |
| /* set up buffer */ | |
| NetApiBufferAllocate(sizeof(USER_INFO_1) + | |
| (name_sz + password_sz + home_dir_sz + | |
| comment_sz + script_path_sz) * sizeof(WCHAR), | |
| (LPVOID *) bufptr); | |
| ui = (PUSER_INFO_1) *bufptr; | |
| ui->usri1_name = (LPWSTR) (ui + 1); | |
| ui->usri1_password = ui->usri1_name + name_sz; | |
| ui->usri1_home_dir = ui->usri1_password + password_sz; | |
| ui->usri1_comment = ui->usri1_home_dir + home_dir_sz; | |
| ui->usri1_script_path = ui->usri1_comment + comment_sz; | |
| /* set data */ | |
| lstrcpyW(ui->usri1_name, ui0->usri0_name); | |
| NetApiBufferFree(ui0); | |
| ui->usri1_password[0] = 0; | |
| ui->usri1_password_age = 0; | |
| ui->usri1_priv = 0; | |
| GetEnvironmentVariableW(homedirW, ui->usri1_home_dir,home_dir_sz); | |
| ui->usri1_comment[0] = 0; | |
| ui->usri1_flags = 0; | |
| ui->usri1_script_path[0] = 0; | |
| break; | |
| } | |
| case 2: | |
| case 3: | |
| case 4: | |
| case 11: | |
| case 20: | |
| case 23: | |
| case 1003: | |
| case 1005: | |
| case 1006: | |
| case 1007: | |
| case 1008: | |
| case 1009: | |
| case 1010: | |
| case 1011: | |
| case 1012: | |
| case 1013: | |
| case 1014: | |
| case 1017: | |
| case 1018: | |
| case 1020: | |
| case 1023: | |
| case 1024: | |
| case 1025: | |
| case 1051: | |
| case 1052: | |
| case 1053: | |
| { | |
| FIXME("Level %d is not implemented\n", level); | |
| return NERR_InternalError; | |
| } | |
| default: | |
| TRACE("Invalid level %d is specified\n", level); | |
| return ERROR_INVALID_LEVEL; | |
| } | |
| return NERR_Success; | |
| } | |
| /************************************************************ | |
| * NetUserGetLocalGroups (NETAPI32.@) | |
| */ | |
| NET_API_STATUS WINAPI | |
| NetUserGetLocalGroups(LPCWSTR servername, LPCWSTR username, DWORD level, | |
| DWORD flags, LPBYTE* bufptr, DWORD prefmaxlen, | |
| LPDWORD entriesread, LPDWORD totalentries) | |
| { | |
| NET_API_STATUS status; | |
| const WCHAR admins[] = {'A','d','m','i','n','i','s','t','r','a','t','o','r','s',0}; | |
| LPWSTR currentuser; | |
| LOCALGROUP_USERS_INFO_0* info; | |
| DWORD size; | |
| FIXME("(%s, %s, %d, %08x, %p %d, %p, %p) stub!\n", | |
| debugstr_w(servername), debugstr_w(username), level, flags, bufptr, | |
| prefmaxlen, entriesread, totalentries); | |
| status = NETAPI_ValidateServername(servername); | |
| if (status != NERR_Success) | |
| return status; | |
| size = UNLEN + 1; | |
| NetApiBufferAllocate(size * sizeof(WCHAR), (LPVOID*)¤tuser); | |
| if (!GetUserNameW(currentuser, &size)) { | |
| NetApiBufferFree(currentuser); | |
| return ERROR_NOT_ENOUGH_MEMORY; | |
| } | |
| if (lstrcmpiW(username, currentuser) && NETAPI_FindUser(username)) | |
| { | |
| NetApiBufferFree(currentuser); | |
| return NERR_UserNotFound; | |
| } | |
| NetApiBufferFree(currentuser); | |
| *totalentries = 1; | |
| size = sizeof(*info) + sizeof(admins); | |
| if(prefmaxlen < size) | |
| status = ERROR_MORE_DATA; | |
| else | |
| status = NetApiBufferAllocate(size, (LPVOID*)&info); | |
| if(status != NERR_Success) | |
| { | |
| *bufptr = NULL; | |
| *entriesread = 0; | |
| return status; | |
| } | |
| info->lgrui0_name = (LPWSTR)((LPBYTE)info + sizeof(*info)); | |
| lstrcpyW(info->lgrui0_name, admins); | |
| *bufptr = (LPBYTE)info; | |
| *entriesread = 1; | |
| return NERR_Success; | |
| } | |
| /************************************************************ | |
| * NetUserEnum (NETAPI32.@) | |
| */ | |
| NET_API_STATUS WINAPI | |
| NetUserEnum(LPCWSTR servername, DWORD level, DWORD filter, LPBYTE* bufptr, | |
| DWORD prefmaxlen, LPDWORD entriesread, LPDWORD totalentries, | |
| LPDWORD resume_handle) | |
| { | |
| NET_API_STATUS status; | |
| WCHAR user[UNLEN + 1]; | |
| DWORD size, len = sizeof(user)/sizeof(user[0]); | |
| TRACE("(%s, %u, 0x%x, %p, %u, %p, %p, %p)\n", debugstr_w(servername), level, | |
| filter, bufptr, prefmaxlen, entriesread, totalentries, resume_handle); | |
| status = NETAPI_ValidateServername(servername); | |
| if (status != NERR_Success) | |
| return status; | |
| if (!NETAPI_IsLocalComputer(servername)) | |
| { | |
| FIXME("Only implemented for local computer, but remote server" | |
| "%s was requested.\n", debugstr_w(servername)); | |
| return NERR_InvalidComputer; | |
| } | |
| if (!GetUserNameW(user, &len)) return GetLastError(); | |
| switch (level) | |
| { | |
| case 0: | |
| { | |
| USER_INFO_0 *info; | |
| size = sizeof(*info) + (strlenW(user) + 1) * sizeof(WCHAR); | |
| if (prefmaxlen < size) | |
| status = ERROR_MORE_DATA; | |
| else | |
| status = NetApiBufferAllocate(size, (void **)&info); | |
| if (status != NERR_Success) | |
| return status; | |
| info->usri0_name = (WCHAR *)((char *)info + sizeof(*info)); | |
| strcpyW(info->usri0_name, user); | |
| *bufptr = (BYTE *)info; | |
| *entriesread = *totalentries = 1; | |
| break; | |
| } | |
| case 20: | |
| { | |
| USER_INFO_20 *info; | |
| SID *sid; | |
| UCHAR *count; | |
| DWORD *rid; | |
| SID_NAME_USE use; | |
| size = sizeof(*info) + (strlenW(user) + 1) * sizeof(WCHAR); | |
| if (prefmaxlen < size) | |
| status = ERROR_MORE_DATA; | |
| else | |
| status = NetApiBufferAllocate(size, (void **)&info); | |
| if (status != NERR_Success) | |
| return status; | |
| size = len = 0; | |
| LookupAccountNameW(NULL, user, NULL, &size, NULL, &len, &use); | |
| if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) | |
| return GetLastError(); | |
| status = NetApiBufferAllocate(size, (void **)&sid); | |
| if (status != NERR_Success) | |
| return status; | |
| if (!LookupAccountNameW(NULL, user, sid, &size, NULL, &len, &use)) | |
| return GetLastError(); | |
| count = GetSidSubAuthorityCount(sid); | |
| rid = GetSidSubAuthority(sid, *count - 1); | |
| info->usri20_name = (WCHAR *)((char *)info + sizeof(*info)); | |
| strcpyW(info->usri20_name, user); | |
| info->usri20_full_name = NULL; | |
| info->usri20_comment = NULL; | |
| info->usri20_flags = UF_NORMAL_ACCOUNT; | |
| info->usri20_user_id = *rid; | |
| *bufptr = (BYTE *)info; | |
| *entriesread = *totalentries = 1; | |
| NetApiBufferFree(sid); | |
| break; | |
| } | |
| default: | |
| FIXME("level %u not supported\n", level); | |
| return ERROR_INVALID_LEVEL; | |
| } | |
| return NERR_Success; | |
| } | |
| /************************************************************ | |
| * ACCESS_QueryAdminDisplayInformation | |
| * | |
| * Creates a buffer with information for the Admin User | |
| */ | |
| static void ACCESS_QueryAdminDisplayInformation(PNET_DISPLAY_USER *buf, PDWORD pdwSize) | |
| { | |
| static const WCHAR sAdminUserName[] = { | |
| 'A','d','m','i','n','i','s','t','r','a','t','o','r',0}; | |
| /* sizes of the field buffers in WCHARS */ | |
| int name_sz, comment_sz, full_name_sz; | |
| PNET_DISPLAY_USER usr; | |
| /* set up buffer */ | |
| name_sz = lstrlenW(sAdminUserName) + 1; | |
| comment_sz = 1; | |
| full_name_sz = 1; | |
| *pdwSize = sizeof(NET_DISPLAY_USER); | |
| *pdwSize += (name_sz + comment_sz + full_name_sz) * sizeof(WCHAR); | |
| NetApiBufferAllocate(*pdwSize, (LPVOID *) buf); | |
| usr = *buf; | |
| usr->usri1_name = (LPWSTR) ((PBYTE) usr + sizeof(NET_DISPLAY_USER)); | |
| usr->usri1_comment = (LPWSTR) ( | |
| ((PBYTE) usr->usri1_name) + name_sz * sizeof(WCHAR)); | |
| usr->usri1_full_name = (LPWSTR) ( | |
| ((PBYTE) usr->usri1_comment) + comment_sz * sizeof(WCHAR)); | |
| /* set data */ | |
| lstrcpyW(usr->usri1_name, sAdminUserName); | |
| usr->usri1_comment[0] = 0; | |
| usr->usri1_flags = UF_SCRIPT | UF_NORMAL_ACCOUNT | UF_DONT_EXPIRE_PASSWD; | |
| usr->usri1_full_name[0] = 0; | |
| usr->usri1_user_id = DOMAIN_USER_RID_ADMIN; | |
| usr->usri1_next_index = 0; | |
| } | |
| /************************************************************ | |
| * ACCESS_QueryGuestDisplayInformation | |
| * | |
| * Creates a buffer with information for the Guest User | |
| */ | |
| static void ACCESS_QueryGuestDisplayInformation(PNET_DISPLAY_USER *buf, PDWORD pdwSize) | |
| { | |
| static const WCHAR sGuestUserName[] = { | |
| 'G','u','e','s','t',0 }; | |
| /* sizes of the field buffers in WCHARS */ | |
| int name_sz, comment_sz, full_name_sz; | |
| PNET_DISPLAY_USER usr; | |
| /* set up buffer */ | |
| name_sz = lstrlenW(sGuestUserName) + 1; | |
| comment_sz = 1; | |
| full_name_sz = 1; | |
| *pdwSize = sizeof(NET_DISPLAY_USER); | |
| *pdwSize += (name_sz + comment_sz + full_name_sz) * sizeof(WCHAR); | |
| NetApiBufferAllocate(*pdwSize, (LPVOID *) buf); | |
| usr = *buf; | |
| usr->usri1_name = (LPWSTR) ((PBYTE) usr + sizeof(NET_DISPLAY_USER)); | |
| usr->usri1_comment = (LPWSTR) ( | |
| ((PBYTE) usr->usri1_name) + name_sz * sizeof(WCHAR)); | |
| usr->usri1_full_name = (LPWSTR) ( | |
| ((PBYTE) usr->usri1_comment) + comment_sz * sizeof(WCHAR)); | |
| /* set data */ | |
| lstrcpyW(usr->usri1_name, sGuestUserName); | |
| usr->usri1_comment[0] = 0; | |
| usr->usri1_flags = UF_ACCOUNTDISABLE | UF_SCRIPT | UF_NORMAL_ACCOUNT | | |
| UF_DONT_EXPIRE_PASSWD; | |
| usr->usri1_full_name[0] = 0; | |
| usr->usri1_user_id = DOMAIN_USER_RID_GUEST; | |
| usr->usri1_next_index = 0; | |
| } | |
| /************************************************************ | |
| * Copies NET_DISPLAY_USER record. | |
| */ | |
| static void ACCESS_CopyDisplayUser(const NET_DISPLAY_USER *dest, LPWSTR *dest_buf, | |
| PNET_DISPLAY_USER src) | |
| { | |
| LPWSTR str = *dest_buf; | |
| src->usri1_name = str; | |
| lstrcpyW(src->usri1_name, dest->usri1_name); | |
| str = (LPWSTR) ( | |
| ((PBYTE) str) + (lstrlenW(str) + 1) * sizeof(WCHAR)); | |
| src->usri1_comment = str; | |
| lstrcpyW(src->usri1_comment, dest->usri1_comment); | |
| str = (LPWSTR) ( | |
| ((PBYTE) str) + (lstrlenW(str) + 1) * sizeof(WCHAR)); | |
| src->usri1_flags = dest->usri1_flags; | |
| src->usri1_full_name = str; | |
| lstrcpyW(src->usri1_full_name, dest->usri1_full_name); | |
| str = (LPWSTR) ( | |
| ((PBYTE) str) + (lstrlenW(str) + 1) * sizeof(WCHAR)); | |
| src->usri1_user_id = dest->usri1_user_id; | |
| src->usri1_next_index = dest->usri1_next_index; | |
| *dest_buf = str; | |
| } | |
| /************************************************************ | |
| * NetQueryDisplayInformation (NETAPI32.@) | |
| * | |
| * The buffer structure: | |
| * - array of fixed size record of the level type | |
| * - strings, referenced by the record of the level type | |
| */ | |
| NET_API_STATUS WINAPI | |
| NetQueryDisplayInformation( | |
| LPCWSTR ServerName, DWORD Level, DWORD Index, DWORD EntriesRequested, | |
| DWORD PreferredMaximumLength, LPDWORD ReturnedEntryCount, | |
| PVOID *SortedBuffer) | |
| { | |
| TRACE("(%s, %d, %d, %d, %d, %p, %p)\n", debugstr_w(ServerName), | |
| Level, Index, EntriesRequested, PreferredMaximumLength, | |
| ReturnedEntryCount, SortedBuffer); | |
| if(!NETAPI_IsLocalComputer(ServerName)) | |
| { | |
| FIXME("Only implemented on local computer, but requested for " | |
| "remote server %s\n", debugstr_w(ServerName)); | |
| return ERROR_ACCESS_DENIED; | |
| } | |
| switch (Level) | |
| { | |
| case 1: | |
| { | |
| /* current record */ | |
| PNET_DISPLAY_USER inf; | |
| /* current available strings buffer */ | |
| LPWSTR str; | |
| PNET_DISPLAY_USER admin, guest; | |
| DWORD admin_size, guest_size; | |
| LPWSTR name = NULL; | |
| DWORD dwSize; | |
| /* sizes of the field buffers in WCHARS */ | |
| int name_sz, comment_sz, full_name_sz; | |
| /* number of the records, returned in SortedBuffer | |
| 3 - for current user, Administrator and Guest users | |
| */ | |
| int records = 3; | |
| FIXME("Level %d partially implemented\n", Level); | |
| *ReturnedEntryCount = records; | |
| comment_sz = 1; | |
| full_name_sz = 1; | |
| /* get data */ | |
| dwSize = UNLEN + 1; | |
| NetApiBufferAllocate(dwSize * sizeof(WCHAR), (LPVOID *) &name); | |
| if (!GetUserNameW(name, &dwSize)) | |
| { | |
| NetApiBufferFree(name); | |
| return ERROR_ACCESS_DENIED; | |
| } | |
| name_sz = dwSize; | |
| ACCESS_QueryAdminDisplayInformation(&admin, &admin_size); | |
| ACCESS_QueryGuestDisplayInformation(&guest, &guest_size); | |
| /* set up buffer */ | |
| dwSize = sizeof(NET_DISPLAY_USER) * records; | |
| dwSize += (name_sz + comment_sz + full_name_sz) * sizeof(WCHAR); | |
| NetApiBufferAllocate(dwSize + | |
| admin_size - sizeof(NET_DISPLAY_USER) + | |
| guest_size - sizeof(NET_DISPLAY_USER), | |
| SortedBuffer); | |
| inf = *SortedBuffer; | |
| str = (LPWSTR) ((PBYTE) inf + sizeof(NET_DISPLAY_USER) * records); | |
| inf->usri1_name = str; | |
| str = (LPWSTR) ( | |
| ((PBYTE) str) + name_sz * sizeof(WCHAR)); | |
| inf->usri1_comment = str; | |
| str = (LPWSTR) ( | |
| ((PBYTE) str) + comment_sz * sizeof(WCHAR)); | |
| inf->usri1_full_name = str; | |
| str = (LPWSTR) ( | |
| ((PBYTE) str) + full_name_sz * sizeof(WCHAR)); | |
| /* set data */ | |
| lstrcpyW(inf->usri1_name, name); | |
| NetApiBufferFree(name); | |
| inf->usri1_comment[0] = 0; | |
| inf->usri1_flags = | |
| UF_SCRIPT | UF_NORMAL_ACCOUNT | UF_DONT_EXPIRE_PASSWD; | |
| inf->usri1_full_name[0] = 0; | |
| inf->usri1_user_id = 0; | |
| inf->usri1_next_index = 0; | |
| inf++; | |
| ACCESS_CopyDisplayUser(admin, &str, inf); | |
| NetApiBufferFree(admin); | |
| inf++; | |
| ACCESS_CopyDisplayUser(guest, &str, inf); | |
| NetApiBufferFree(guest); | |
| break; | |
| } | |
| case 2: | |
| case 3: | |
| { | |
| FIXME("Level %d is not implemented\n", Level); | |
| break; | |
| } | |
| default: | |
| TRACE("Invalid level %d is specified\n", Level); | |
| return ERROR_INVALID_LEVEL; | |
| } | |
| return NERR_Success; | |
| } | |
| /************************************************************ | |
| * NetGetDCName (NETAPI32.@) | |
| * | |
| * Return the name of the primary domain controller (PDC) | |
| */ | |
| NET_API_STATUS WINAPI | |
| NetGetDCName(LPCWSTR servername, LPCWSTR domainname, LPBYTE *bufptr) | |
| { | |
| FIXME("(%s, %s, %p) stub!\n", debugstr_w(servername), | |
| debugstr_w(domainname), bufptr); | |
| return NERR_DCNotFound; /* say we can't find a domain controller */ | |
| } | |
| /************************************************************ | |
| * NetGetAnyDCName (NETAPI32.@) | |
| * | |
| * Return the name of any domain controller (DC) for a | |
| * domain that is directly trusted by the specified server | |
| */ | |
| NET_API_STATUS WINAPI NetGetAnyDCName(LPCWSTR servername, LPCWSTR domainname, LPBYTE *bufptr) | |
| { | |
| FIXME("(%s, %s, %p) stub!\n", debugstr_w(servername), | |
| debugstr_w(domainname), bufptr); | |
| return ERROR_NO_SUCH_DOMAIN; | |
| } | |
| /************************************************************ | |
| * NetGroupAddUser (NETAPI32.@) | |
| */ | |
| NET_API_STATUS WINAPI | |
| NetGroupAddUser(LPCWSTR servername, LPCWSTR groupname, LPCWSTR username) | |
| { | |
| FIXME("(%s, %s, %s) stub!\n", debugstr_w(servername), | |
| debugstr_w(groupname), debugstr_w(username)); | |
| return NERR_Success; | |
| } | |
| /************************************************************ | |
| * NetGroupEnum (NETAPI32.@) | |
| * | |
| */ | |
| NET_API_STATUS WINAPI | |
| NetGroupEnum(LPCWSTR servername, DWORD level, LPBYTE *bufptr, DWORD prefmaxlen, | |
| LPDWORD entriesread, LPDWORD totalentries, LPDWORD resume_handle) | |
| { | |
| FIXME("(%s, %d, %p, %d, %p, %p, %p) stub!\n", debugstr_w(servername), | |
| level, bufptr, prefmaxlen, entriesread, totalentries, resume_handle); | |
| return ERROR_ACCESS_DENIED; | |
| } | |
| /************************************************************ | |
| * NetGroupGetInfo (NETAPI32.@) | |
| * | |
| */ | |
| NET_API_STATUS WINAPI NetGroupGetInfo(LPCWSTR servername, LPCWSTR groupname, DWORD level, LPBYTE *bufptr) | |
| { | |
| FIXME("(%s, %s, %d, %p) stub!\n", debugstr_w(servername), debugstr_w(groupname), level, bufptr); | |
| return ERROR_ACCESS_DENIED; | |
| } | |
| /****************************************************************************** | |
| * NetUserModalsGet (NETAPI32.@) | |
| * | |
| * Retrieves global information for all users and global groups in the security | |
| * database. | |
| * | |
| * PARAMS | |
| * szServer [I] Specifies the DNS or the NetBIOS name of the remote server | |
| * on which the function is to execute. | |
| * level [I] Information level of the data. | |
| * 0 Return global passwords parameters. bufptr points to a | |
| * USER_MODALS_INFO_0 struct. | |
| * 1 Return logon server and domain controller information. bufptr | |
| * points to a USER_MODALS_INFO_1 struct. | |
| * 2 Return domain name and identifier. bufptr points to a | |
| * USER_MODALS_INFO_2 struct. | |
| * 3 Return lockout information. bufptr points to a USER_MODALS_INFO_3 | |
| * struct. | |
| * pbuffer [I] Buffer that receives the data. | |
| * | |
| * RETURNS | |
| * Success: NERR_Success. | |
| * Failure: | |
| * ERROR_ACCESS_DENIED - the user does not have access to the info. | |
| * NERR_InvalidComputer - computer name is invalid. | |
| */ | |
| NET_API_STATUS WINAPI NetUserModalsGet( | |
| LPCWSTR szServer, DWORD level, LPBYTE *pbuffer) | |
| { | |
| TRACE("(%s %d %p)\n", debugstr_w(szServer), level, pbuffer); | |
| switch (level) | |
| { | |
| case 0: | |
| /* return global passwords parameters */ | |
| FIXME("level 0 not implemented!\n"); | |
| *pbuffer = NULL; | |
| return NERR_InternalError; | |
| case 1: | |
| /* return logon server and domain controller info */ | |
| FIXME("level 1 not implemented!\n"); | |
| *pbuffer = NULL; | |
| return NERR_InternalError; | |
| case 2: | |
| { | |
| /* return domain name and identifier */ | |
| PUSER_MODALS_INFO_2 umi; | |
| LSA_HANDLE policyHandle; | |
| LSA_OBJECT_ATTRIBUTES objectAttributes; | |
| PPOLICY_ACCOUNT_DOMAIN_INFO domainInfo; | |
| NTSTATUS ntStatus; | |
| PSID domainIdentifier = NULL; | |
| int domainNameLen; | |
| ZeroMemory(&objectAttributes, sizeof(objectAttributes)); | |
| objectAttributes.Length = sizeof(objectAttributes); | |
| ntStatus = LsaOpenPolicy(NULL, &objectAttributes, | |
| POLICY_VIEW_LOCAL_INFORMATION, | |
| &policyHandle); | |
| if (ntStatus != STATUS_SUCCESS) | |
| { | |
| WARN("LsaOpenPolicy failed with NT status %x\n", | |
| LsaNtStatusToWinError(ntStatus)); | |
| return ntStatus; | |
| } | |
| ntStatus = LsaQueryInformationPolicy(policyHandle, | |
| PolicyAccountDomainInformation, | |
| (PVOID *)&domainInfo); | |
| if (ntStatus != STATUS_SUCCESS) | |
| { | |
| WARN("LsaQueryInformationPolicy failed with NT status %x\n", | |
| LsaNtStatusToWinError(ntStatus)); | |
| LsaClose(policyHandle); | |
| return ntStatus; | |
| } | |
| domainIdentifier = domainInfo->DomainSid; | |
| domainNameLen = lstrlenW(domainInfo->DomainName.Buffer) + 1; | |
| LsaClose(policyHandle); | |
| ntStatus = NetApiBufferAllocate(sizeof(USER_MODALS_INFO_2) + | |
| GetLengthSid(domainIdentifier) + | |
| domainNameLen * sizeof(WCHAR), | |
| (LPVOID *)pbuffer); | |
| if (ntStatus != NERR_Success) | |
| { | |
| WARN("NetApiBufferAllocate() failed\n"); | |
| LsaFreeMemory(domainInfo); | |
| return ntStatus; | |
| } | |
| umi = (USER_MODALS_INFO_2 *) *pbuffer; | |
| umi->usrmod2_domain_id = *pbuffer + sizeof(USER_MODALS_INFO_2); | |
| umi->usrmod2_domain_name = (LPWSTR)(*pbuffer + | |
| sizeof(USER_MODALS_INFO_2) + GetLengthSid(domainIdentifier)); | |
| lstrcpynW(umi->usrmod2_domain_name, | |
| domainInfo->DomainName.Buffer, | |
| domainNameLen); | |
| CopySid(GetLengthSid(domainIdentifier), umi->usrmod2_domain_id, | |
| domainIdentifier); | |
| LsaFreeMemory(domainInfo); | |
| break; | |
| } | |
| case 3: | |
| /* return lockout information */ | |
| FIXME("level 3 not implemented!\n"); | |
| *pbuffer = NULL; | |
| return NERR_InternalError; | |
| default: | |
| TRACE("Invalid level %d is specified\n", level); | |
| *pbuffer = NULL; | |
| return ERROR_INVALID_LEVEL; | |
| } | |
| return NERR_Success; | |
| } | |
| static NET_API_STATUS change_password_smb( LPCWSTR domainname, LPCWSTR username, | |
| LPCWSTR oldpassword, LPCWSTR newpassword ) | |
| { | |
| #ifdef HAVE_FORK | |
| NET_API_STATUS ret = NERR_Success; | |
| static char option_silent[] = "-s"; | |
| static char option_user[] = "-U"; | |
| static char option_remote[] = "-r"; | |
| static char smbpasswd[] = "smbpasswd"; | |
| int pipe_out[2]; | |
| pid_t pid, wret; | |
| int status; | |
| char *server = NULL, *user, *argv[7], *old = NULL, *new = NULL; | |
| if (domainname && !(server = strdup_unixcp( domainname ))) return ERROR_OUTOFMEMORY; | |
| if (!(user = strdup_unixcp( username ))) | |
| { | |
| ret = ERROR_OUTOFMEMORY; | |
| goto end; | |
| } | |
| if (!(old = strdup_unixcp( oldpassword ))) | |
| { | |
| ret = ERROR_OUTOFMEMORY; | |
| goto end; | |
| } | |
| if (!(new = strdup_unixcp( newpassword ))) | |
| { | |
| ret = ERROR_OUTOFMEMORY; | |
| goto end; | |
| } | |
| argv[0] = smbpasswd; | |
| argv[1] = option_silent; | |
| argv[2] = option_user; | |
| argv[3] = user; | |
| if (server) | |
| { | |
| argv[4] = option_remote; | |
| argv[5] = server; | |
| argv[6] = NULL; | |
| } | |
| else argv[4] = NULL; | |
| if (pipe( pipe_out ) == -1) | |
| { | |
| ret = NERR_InternalError; | |
| goto end; | |
| } | |
| fcntl( pipe_out[0], F_SETFD, FD_CLOEXEC ); | |
| fcntl( pipe_out[1], F_SETFD, FD_CLOEXEC ); | |
| switch ((pid = fork())) | |
| { | |
| case -1: | |
| close( pipe_out[0] ); | |
| close( pipe_out[1] ); | |
| ret = NERR_InternalError; | |
| goto end; | |
| case 0: | |
| dup2( pipe_out[0], 0 ); | |
| close( pipe_out[0] ); | |
| close( pipe_out[1] ); | |
| execvp( "smbpasswd", argv ); | |
| ERR( "can't execute smbpasswd, is it installed?\n" ); | |
| _exit(1); | |
| default: | |
| close( pipe_out[0] ); | |
| break; | |
| } | |
| write( pipe_out[1], old, strlen( old ) ); | |
| write( pipe_out[1], "\n", 1 ); | |
| write( pipe_out[1], new, strlen( new ) ); | |
| write( pipe_out[1], "\n", 1 ); | |
| write( pipe_out[1], new, strlen( new ) ); | |
| write( pipe_out[1], "\n", 1 ); | |
| close( pipe_out[1] ); | |
| do { | |
| wret = waitpid(pid, &status, 0); | |
| } while (wret < 0 && errno == EINTR); | |
| if (ret == NERR_Success && (wret < 0 || !WIFEXITED(status) || WEXITSTATUS(status))) | |
| ret = NERR_InternalError; | |
| end: | |
| HeapFree( GetProcessHeap(), 0, server ); | |
| HeapFree( GetProcessHeap(), 0, user ); | |
| HeapFree( GetProcessHeap(), 0, old ); | |
| HeapFree( GetProcessHeap(), 0, new ); | |
| return ret; | |
| #else | |
| ERR( "no fork support on this platform\n" ); | |
| return NERR_InternalError; | |
| #endif | |
| } | |
| /****************************************************************************** | |
| * NetUserChangePassword (NETAPI32.@) | |
| * PARAMS | |
| * domainname [I] Optional. Domain on which the user resides or the logon | |
| * domain of the current user if NULL. | |
| * username [I] Optional. Username to change the password for or the name | |
| * of the current user if NULL. | |
| * oldpassword [I] The user's current password. | |
| * newpassword [I] The password that the user will be changed to using. | |
| * | |
| * RETURNS | |
| * Success: NERR_Success. | |
| * Failure: NERR_* failure code or win error code. | |
| * | |
| */ | |
| NET_API_STATUS WINAPI NetUserChangePassword(LPCWSTR domainname, LPCWSTR username, | |
| LPCWSTR oldpassword, LPCWSTR newpassword) | |
| { | |
| struct sam_user *user; | |
| TRACE("(%s, %s, ..., ...)\n", debugstr_w(domainname), debugstr_w(username)); | |
| if (!change_password_smb( domainname, username, oldpassword, newpassword )) | |
| return NERR_Success; | |
| if(domainname) | |
| FIXME("Ignoring domainname %s.\n", debugstr_w(domainname)); | |
| if((user = NETAPI_FindUser(username)) == NULL) | |
| return NERR_UserNotFound; | |
| if(lstrcmpW(user->user_password, oldpassword) != 0) | |
| return ERROR_INVALID_PASSWORD; | |
| if(lstrlenW(newpassword) > PWLEN) | |
| return ERROR_PASSWORD_RESTRICTION; | |
| lstrcpyW(user->user_password, newpassword); | |
| return NERR_Success; | |
| } | |
| NET_API_STATUS WINAPI NetUseAdd(LMSTR servername, DWORD level, LPBYTE bufptr, LPDWORD parm_err) | |
| { | |
| FIXME("%s %d %p %p stub\n", debugstr_w(servername), level, bufptr, parm_err); | |
| return NERR_Success; | |
| } | |
| NET_API_STATUS WINAPI NetUseDel(LMSTR servername, LMSTR usename, DWORD forcecond) | |
| { | |
| FIXME("%s %s %d stub\n", debugstr_w(servername), debugstr_w(usename), forcecond); | |
| return NERR_Success; | |
| } | |
| /************************************************************ | |
| * I_BrowserSetNetlogonState (NETAPI32.@) | |
| */ | |
| NET_API_STATUS WINAPI I_BrowserSetNetlogonState( | |
| LPWSTR ServerName, LPWSTR DomainName, LPWSTR EmulatedServerName, | |
| DWORD Role) | |
| { | |
| return ERROR_NOT_SUPPORTED; | |
| } | |
| /************************************************************ | |
| * I_BrowserQueryEmulatedDomains (NETAPI32.@) | |
| */ | |
| NET_API_STATUS WINAPI I_BrowserQueryEmulatedDomains( | |
| LPWSTR ServerName, PBROWSER_EMULATED_DOMAIN *EmulatedDomains, | |
| LPDWORD EntriesRead) | |
| { | |
| return ERROR_NOT_SUPPORTED; | |
| } | |
| DWORD WINAPI DsGetDcNameW(LPCWSTR ComputerName, LPCWSTR AvoidDCName, | |
| GUID* DomainGuid, LPCWSTR SiteName, ULONG Flags, | |
| PDOMAIN_CONTROLLER_INFOW *DomainControllerInfo) | |
| { | |
| FIXME("(%s, %s, %s, %s, %08x, %p): stub\n", debugstr_w(ComputerName), | |
| debugstr_w(AvoidDCName), debugstr_guid(DomainGuid), | |
| debugstr_w(SiteName), Flags, DomainControllerInfo); | |
| return ERROR_CALL_NOT_IMPLEMENTED; | |
| } | |
| DWORD WINAPI DsGetDcNameA(LPCSTR ComputerName, LPCSTR AvoidDCName, | |
| GUID* DomainGuid, LPCSTR SiteName, ULONG Flags, | |
| PDOMAIN_CONTROLLER_INFOA *DomainControllerInfo) | |
| { | |
| FIXME("(%s, %s, %s, %s, %08x, %p): stub\n", debugstr_a(ComputerName), | |
| debugstr_a(AvoidDCName), debugstr_guid(DomainGuid), | |
| debugstr_a(SiteName), Flags, DomainControllerInfo); | |
| return ERROR_CALL_NOT_IMPLEMENTED; | |
| } | |
| DWORD WINAPI DsGetSiteNameW(LPCWSTR ComputerName, LPWSTR *SiteName) | |
| { | |
| FIXME("(%s, %p): stub\n", debugstr_w(ComputerName), SiteName); | |
| return ERROR_CALL_NOT_IMPLEMENTED; | |
| } | |
| DWORD WINAPI DsGetSiteNameA(LPCSTR ComputerName, LPSTR *SiteName) | |
| { | |
| FIXME("(%s, %p): stub\n", debugstr_a(ComputerName), SiteName); | |
| return ERROR_CALL_NOT_IMPLEMENTED; | |
| } | |
| /************************************************************ | |
| * DsRoleFreeMemory (NETAPI32.@) | |
| * | |
| * PARAMS | |
| * Buffer [I] Pointer to the to-be-freed buffer. | |
| * | |
| * RETURNS | |
| * Nothing | |
| */ | |
| VOID WINAPI DsRoleFreeMemory(PVOID Buffer) | |
| { | |
| TRACE("(%p)\n", Buffer); | |
| HeapFree(GetProcessHeap(), 0, Buffer); | |
| } | |
| /************************************************************ | |
| * DsRoleGetPrimaryDomainInformation (NETAPI32.@) | |
| * | |
| * PARAMS | |
| * lpServer [I] Pointer to UNICODE string with ComputerName | |
| * InfoLevel [I] Type of data to retrieve | |
| * Buffer [O] Pointer to to the requested data | |
| * | |
| * RETURNS | |
| * | |
| * NOTES | |
| * When lpServer is NULL, use the local computer | |
| */ | |
| DWORD WINAPI DsRoleGetPrimaryDomainInformation( | |
| LPCWSTR lpServer, DSROLE_PRIMARY_DOMAIN_INFO_LEVEL InfoLevel, | |
| PBYTE* Buffer) | |
| { | |
| DWORD ret; | |
| FIXME("(%p, %d, %p) stub\n", lpServer, InfoLevel, Buffer); | |
| /* Check some input parameters */ | |
| if (!Buffer) return ERROR_INVALID_PARAMETER; | |
| if ((InfoLevel < DsRolePrimaryDomainInfoBasic) || (InfoLevel > DsRoleOperationState)) return ERROR_INVALID_PARAMETER; | |
| *Buffer = NULL; | |
| switch (InfoLevel) | |
| { | |
| case DsRolePrimaryDomainInfoBasic: | |
| { | |
| LSA_OBJECT_ATTRIBUTES ObjectAttributes; | |
| LSA_HANDLE PolicyHandle; | |
| PPOLICY_ACCOUNT_DOMAIN_INFO DomainInfo; | |
| NTSTATUS NtStatus; | |
| int logon_domain_sz; | |
| DWORD size; | |
| PDSROLE_PRIMARY_DOMAIN_INFO_BASIC basic; | |
| ZeroMemory(&ObjectAttributes, sizeof(ObjectAttributes)); | |
| NtStatus = LsaOpenPolicy(NULL, &ObjectAttributes, | |
| POLICY_VIEW_LOCAL_INFORMATION, &PolicyHandle); | |
| if (NtStatus != STATUS_SUCCESS) | |
| { | |
| TRACE("LsaOpenPolicyFailed with NT status %x\n", | |
| LsaNtStatusToWinError(NtStatus)); | |
| return ERROR_OUTOFMEMORY; | |
| } | |
| LsaQueryInformationPolicy(PolicyHandle, | |
| PolicyAccountDomainInformation, (PVOID*)&DomainInfo); | |
| logon_domain_sz = lstrlenW(DomainInfo->DomainName.Buffer) + 1; | |
| LsaClose(PolicyHandle); | |
| size = sizeof(DSROLE_PRIMARY_DOMAIN_INFO_BASIC) + | |
| logon_domain_sz * sizeof(WCHAR); | |
| basic = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size); | |
| if (basic) | |
| { | |
| basic->MachineRole = DsRole_RoleStandaloneWorkstation; | |
| basic->DomainNameFlat = (LPWSTR)((LPBYTE)basic + | |
| sizeof(DSROLE_PRIMARY_DOMAIN_INFO_BASIC)); | |
| lstrcpyW(basic->DomainNameFlat, DomainInfo->DomainName.Buffer); | |
| ret = ERROR_SUCCESS; | |
| } | |
| else | |
| ret = ERROR_OUTOFMEMORY; | |
| *Buffer = (PBYTE)basic; | |
| LsaFreeMemory(DomainInfo); | |
| } | |
| break; | |
| default: | |
| ret = ERROR_CALL_NOT_IMPLEMENTED; | |
| } | |
| return ret; | |
| } | |
| /************************************************************ | |
| * NetLocalGroupAdd (NETAPI32.@) | |
| */ | |
| NET_API_STATUS WINAPI NetLocalGroupAdd( | |
| LPCWSTR servername, | |
| DWORD level, | |
| LPBYTE buf, | |
| LPDWORD parm_err) | |
| { | |
| FIXME("(%s %d %p %p) stub!\n", debugstr_w(servername), level, buf, | |
| parm_err); | |
| return NERR_Success; | |
| } | |
| /************************************************************ | |
| * NetLocalGroupAddMember (NETAPI32.@) | |
| */ | |
| NET_API_STATUS WINAPI NetLocalGroupAddMember( | |
| LPCWSTR servername, | |
| LPCWSTR groupname, | |
| PSID membersid) | |
| { | |
| FIXME("(%s %s %p) stub!\n", debugstr_w(servername), | |
| debugstr_w(groupname), membersid); | |
| return NERR_Success; | |
| } | |
| /************************************************************ | |
| * NetLocalGroupAddMembers (NETAPI32.@) | |
| */ | |
| NET_API_STATUS WINAPI NetLocalGroupAddMembers( | |
| LPCWSTR servername, | |
| LPCWSTR groupname, | |
| DWORD level, | |
| LPBYTE buf, | |
| DWORD totalentries) | |
| { | |
| FIXME("(%s %s %d %p %d) stub!\n", debugstr_w(servername), | |
| debugstr_w(groupname), level, buf, totalentries); | |
| return NERR_Success; | |
| } | |
| /************************************************************ | |
| * NetLocalGroupDel (NETAPI32.@) | |
| */ | |
| NET_API_STATUS WINAPI NetLocalGroupDel( | |
| LPCWSTR servername, | |
| LPCWSTR groupname) | |
| { | |
| FIXME("(%s %s) stub!\n", debugstr_w(servername), debugstr_w(groupname)); | |
| return NERR_Success; | |
| } | |
| /************************************************************ | |
| * NetLocalGroupDelMember (NETAPI32.@) | |
| */ | |
| NET_API_STATUS WINAPI NetLocalGroupDelMember( | |
| LPCWSTR servername, | |
| LPCWSTR groupname, | |
| PSID membersid) | |
| { | |
| FIXME("(%s %s %p) stub!\n", debugstr_w(servername), | |
| debugstr_w(groupname), membersid); | |
| return NERR_Success; | |
| } | |
| /************************************************************ | |
| * NetLocalGroupDelMembers (NETAPI32.@) | |
| */ | |
| NET_API_STATUS WINAPI NetLocalGroupDelMembers( | |
| LPCWSTR servername, | |
| LPCWSTR groupname, | |
| DWORD level, | |
| LPBYTE buf, | |
| DWORD totalentries) | |
| { | |
| FIXME("(%s %s %d %p %d) stub!\n", debugstr_w(servername), | |
| debugstr_w(groupname), level, buf, totalentries); | |
| return NERR_Success; | |
| } | |
| /************************************************************ | |
| * NetLocalGroupEnum (NETAPI32.@) | |
| */ | |
| NET_API_STATUS WINAPI NetLocalGroupEnum( | |
| LPCWSTR servername, | |
| DWORD level, | |
| LPBYTE* bufptr, | |
| DWORD prefmaxlen, | |
| LPDWORD entriesread, | |
| LPDWORD totalentries, | |
| PDWORD_PTR resumehandle) | |
| { | |
| FIXME("(%s %d %p %d %p %p %p) stub!\n", debugstr_w(servername), | |
| level, bufptr, prefmaxlen, entriesread, totalentries, resumehandle); | |
| *entriesread = 0; | |
| *totalentries = 0; | |
| return NERR_Success; | |
| } | |
| /************************************************************ | |
| * NetLocalGroupGetInfo (NETAPI32.@) | |
| */ | |
| NET_API_STATUS WINAPI NetLocalGroupGetInfo( | |
| LPCWSTR servername, | |
| LPCWSTR groupname, | |
| DWORD level, | |
| LPBYTE* bufptr) | |
| { | |
| static const WCHAR commentW[]={'N','o',' ','c','o','m','m','e','n','t',0}; | |
| LOCALGROUP_INFO_1* info; | |
| DWORD size; | |
| FIXME("(%s %s %d %p) semi-stub!\n", debugstr_w(servername), | |
| debugstr_w(groupname), level, bufptr); | |
| size = sizeof(*info) + sizeof(WCHAR) * (lstrlenW(groupname)+1) + sizeof(commentW); | |
| NetApiBufferAllocate(size, (LPVOID*)&info); | |
| info->lgrpi1_name = (LPWSTR)(info + 1); | |
| lstrcpyW(info->lgrpi1_name, groupname); | |
| info->lgrpi1_comment = info->lgrpi1_name + lstrlenW(groupname) + 1; | |
| lstrcpyW(info->lgrpi1_comment, commentW); | |
| *bufptr = (LPBYTE)info; | |
| return NERR_Success; | |
| } | |
| /************************************************************ | |
| * NetLocalGroupGetMembers (NETAPI32.@) | |
| */ | |
| NET_API_STATUS WINAPI NetLocalGroupGetMembers( | |
| LPCWSTR servername, | |
| LPCWSTR localgroupname, | |
| DWORD level, | |
| LPBYTE* bufptr, | |
| DWORD prefmaxlen, | |
| LPDWORD entriesread, | |
| LPDWORD totalentries, | |
| PDWORD_PTR resumehandle) | |
| { | |
| FIXME("(%s %s %d %p %d, %p %p %p) stub!\n", debugstr_w(servername), | |
| debugstr_w(localgroupname), level, bufptr, prefmaxlen, entriesread, | |
| totalentries, resumehandle); | |
| if (level == 3) | |
| { | |
| WCHAR userName[MAX_COMPUTERNAME_LENGTH + 1]; | |
| DWORD userNameLen; | |
| DWORD len,needlen; | |
| PLOCALGROUP_MEMBERS_INFO_3 ptr; | |
| /* still a stub, current user is belonging to all groups */ | |
| *totalentries = 1; | |
| *entriesread = 0; | |
| userNameLen = MAX_COMPUTERNAME_LENGTH + 1; | |
| if (!GetUserNameW(userName,&userNameLen)) | |
| return ERROR_NOT_ENOUGH_MEMORY; | |
| needlen = sizeof(LOCALGROUP_MEMBERS_INFO_3) + | |
| (userNameLen+2) * sizeof(WCHAR); | |
| if (prefmaxlen != MAX_PREFERRED_LENGTH) | |
| len = min(prefmaxlen,needlen); | |
| else | |
| len = needlen; | |
| NetApiBufferAllocate(len, (LPVOID *) bufptr); | |
| if (len < needlen) | |
| return ERROR_MORE_DATA; | |
| ptr = (PLOCALGROUP_MEMBERS_INFO_3)*bufptr; | |
| ptr->lgrmi3_domainandname = (LPWSTR)(*bufptr+sizeof(LOCALGROUP_MEMBERS_INFO_3)); | |
| lstrcpyW(ptr->lgrmi3_domainandname,userName); | |
| *entriesread = 1; | |
| } | |
| return NERR_Success; | |
| } | |
| /************************************************************ | |
| * NetLocalGroupSetInfo (NETAPI32.@) | |
| */ | |
| NET_API_STATUS WINAPI NetLocalGroupSetInfo( | |
| LPCWSTR servername, | |
| LPCWSTR groupname, | |
| DWORD level, | |
| LPBYTE buf, | |
| LPDWORD parm_err) | |
| { | |
| FIXME("(%s %s %d %p %p) stub!\n", debugstr_w(servername), | |
| debugstr_w(groupname), level, buf, parm_err); | |
| return NERR_Success; | |
| } | |
| /************************************************************ | |
| * NetLocalGroupSetMember (NETAPI32.@) | |
| */ | |
| NET_API_STATUS WINAPI NetLocalGroupSetMembers( | |
| LPCWSTR servername, | |
| LPCWSTR groupname, | |
| DWORD level, | |
| LPBYTE buf, | |
| DWORD totalentries) | |
| { | |
| FIXME("(%s %s %d %p %d) stub!\n", debugstr_w(servername), | |
| debugstr_w(groupname), level, buf, totalentries); | |
| return NERR_Success; | |
| } | |
| /************************************************************ | |
| * DavGetHTTPFromUNCPath (NETAPI32.@) | |
| */ | |
| DWORD WINAPI DavGetHTTPFromUNCPath(const WCHAR *unc_path, WCHAR *buf, DWORD *buflen) | |
| { | |
| static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0}; | |
| static const WCHAR httpsW[] = {'h','t','t','p','s',':','/','/',0}; | |
| static const WCHAR sslW[] = {'S','S','L',0}; | |
| static const WCHAR fmtW[] = {':','%','u',0}; | |
| const WCHAR *p = unc_path, *q, *server, *path, *scheme = httpW; | |
| UINT i, len_server, len_path = 0, len_port = 0, len, port = 0; | |
| WCHAR *end, portbuf[12]; | |
| TRACE("(%s %p %p)\n", debugstr_w(unc_path), buf, buflen); | |
| if (p[0] != '\\' || p[1] != '\\' || !p[2]) return ERROR_INVALID_PARAMETER; | |
| q = p += 2; | |
| while (*q && *q != '\\' && *q != '/' && *q != '@') q++; | |
| server = p; | |
| len_server = q - p; | |
| if (*q == '@') | |
| { | |
| p = ++q; | |
| while (*p && (*p != '\\' && *p != '/' && *p != '@')) p++; | |
| if (p - q == 3 && !memicmpW( q, sslW, 3 )) | |
| { | |
| scheme = httpsW; | |
| q = p; | |
| } | |
| else if ((port = strtolW( q, &end, 10 ))) q = end; | |
| else return ERROR_INVALID_PARAMETER; | |
| } | |
| if (*q == '@') | |
| { | |
| if (!(port = strtolW( ++q, &end, 10 ))) return ERROR_INVALID_PARAMETER; | |
| q = end; | |
| } | |
| if (*q == '\\' || *q == '/') q++; | |
| path = q; | |
| while (*q++) len_path++; | |
| if (len_path && (path[len_path - 1] == '\\' || path[len_path - 1] == '/')) | |
| len_path--; /* remove trailing slash */ | |
| sprintfW( portbuf, fmtW, port ); | |
| if (scheme == httpsW) | |
| { | |
| len = strlenW( httpsW ); | |
| if (port && port != 443) len_port = strlenW( portbuf ); | |
| } | |
| else | |
| { | |
| len = strlenW( httpW ); | |
| if (port && port != 80) len_port = strlenW( portbuf ); | |
| } | |
| len += len_server; | |
| len += len_port; | |
| if (len_path) len += len_path + 1; /* leading '/' */ | |
| len++; /* nul */ | |
| if (*buflen < len) | |
| { | |
| *buflen = len; | |
| return ERROR_INSUFFICIENT_BUFFER; | |
| } | |
| memcpy( buf, scheme, strlenW(scheme) * sizeof(WCHAR) ); | |
| buf += strlenW( scheme ); | |
| memcpy( buf, server, len_server * sizeof(WCHAR) ); | |
| buf += len_server; | |
| if (len_port) | |
| { | |
| memcpy( buf, portbuf, len_port * sizeof(WCHAR) ); | |
| buf += len_port; | |
| } | |
| if (len_path) | |
| { | |
| *buf++ = '/'; | |
| for (i = 0; i < len_path; i++) | |
| { | |
| if (path[i] == '\\') *buf++ = '/'; | |
| else *buf++ = path[i]; | |
| } | |
| } | |
| *buf = 0; | |
| *buflen = len; | |
| return ERROR_SUCCESS; | |
| } | |
| /************************************************************ | |
| * DavGetUNCFromHTTPPath (NETAPI32.@) | |
| */ | |
| DWORD WINAPI DavGetUNCFromHTTPPath(const WCHAR *http_path, WCHAR *buf, DWORD *buflen) | |
| { | |
| static const WCHAR httpW[] = {'h','t','t','p'}; | |
| static const WCHAR httpsW[] = {'h','t','t','p','s'}; | |
| static const WCHAR davrootW[] = {'\\','D','a','v','W','W','W','R','o','o','t'}; | |
| static const WCHAR sslW[] = {'@','S','S','L'}; | |
| static const WCHAR port80W[] = {'8','0'}; | |
| static const WCHAR port443W[] = {'4','4','3'}; | |
| const WCHAR *p = http_path, *server, *port = NULL, *path = NULL; | |
| DWORD i, len = 0, len_server = 0, len_port = 0, len_path = 0; | |
| BOOL ssl; | |
| TRACE("(%s %p %p)\n", debugstr_w(http_path), buf, buflen); | |
| while (*p && *p != ':') { p++; len++; }; | |
| if (len == sizeof(httpW)/sizeof(httpW[0]) && !memicmpW( http_path, httpW, len )) ssl = FALSE; | |
| else if (len == sizeof(httpsW)/sizeof(httpsW[0]) && !memicmpW( http_path, httpsW, len )) ssl = TRUE; | |
| else return ERROR_INVALID_PARAMETER; | |
| if (p[0] != ':' || p[1] != '/' || p[2] != '/') return ERROR_INVALID_PARAMETER; | |
| server = p += 3; | |
| while (*p && *p != ':' && *p != '/') { p++; len_server++; }; | |
| if (!len_server) return ERROR_BAD_NET_NAME; | |
| if (*p == ':') | |
| { | |
| port = ++p; | |
| while (*p && isdigitW(*p)) { p++; len_port++; }; | |
| if (len_port == 2 && !ssl && !memcmp( port, port80W, sizeof(port80W) )) port = NULL; | |
| else if (len_port == 3 && ssl && !memcmp( port, port443W, sizeof(port443W) )) port = NULL; | |
| path = p; | |
| } | |
| else if (*p == '/') path = p; | |
| while (*p) | |
| { | |
| if (p[0] == '/' && p[1] == '/') return ERROR_BAD_NET_NAME; | |
| p++; len_path++; | |
| } | |
| if (len_path && path[len_path - 1] == '/') len_path--; | |
| len = len_server + 2; /* \\ */ | |
| if (ssl) len += 4; /* @SSL */ | |
| if (port) len += len_port + 1 /* @ */; | |
| len += sizeof(davrootW)/sizeof(davrootW[0]); | |
| len += len_path + 1; /* nul */ | |
| if (*buflen < len) | |
| { | |
| *buflen = len; | |
| return ERROR_INSUFFICIENT_BUFFER; | |
| } | |
| buf[0] = buf[1] = '\\'; | |
| buf += 2; | |
| memcpy( buf, server, len_server * sizeof(WCHAR) ); | |
| buf += len_server; | |
| if (ssl) | |
| { | |
| memcpy( buf, sslW, sizeof(sslW) ); | |
| buf += 4; | |
| } | |
| if (port) | |
| { | |
| *buf++ = '@'; | |
| memcpy( buf, port, len_port * sizeof(WCHAR) ); | |
| buf += len_port; | |
| } | |
| memcpy( buf, davrootW, sizeof(davrootW) ); | |
| buf += sizeof(davrootW)/sizeof(davrootW[0]); | |
| for (i = 0; i < len_path; i++) | |
| { | |
| if (path[i] == '/') *buf++ = '\\'; | |
| else *buf++ = path[i]; | |
| } | |
| *buf = 0; | |
| *buflen = len; | |
| return ERROR_SUCCESS; | |
| } |