/* 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 * Copyright 2020 Dmitry Timoshkov * * 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 #include #include #ifdef HAVE_SYS_WAIT_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include "ntstatus.h" #define WIN32_NO_STATUS #include "windef.h" #include "winbase.h" #include "winsock2.h" #include "ws2ipdef.h" #include "windns.h" #include "lm.h" #include "lmaccess.h" #include "atsvc.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/list.h" #include "wine/unicode.h" #include "initguid.h" WINE_DEFAULT_DEBUG_CHANNEL(netapi32); DEFINE_GUID(GUID_NULL,0,0,0,0,0,0,0,0,0,0,0); 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) { if (libnetapi_handle) return TRUE; if (!(libnetapi_handle = dlopen( SONAME_LIBNETAPI, RTLD_NOW ))) { WARN( "Failed to load libnetapi: %s\n", dlerror() ); return FALSE; } #define LOAD_FUNCPTR(f) \ if (!(p##f = dlsym( libnetapi_handle, #f ))) \ { \ ERR( "Failed to load %s: %s\n", #f, dlerror() ); \ 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: dlclose( libnetapi_handle ); 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 = ARRAY_SIZE(buf); 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) { TRACE("(%s, %p, %p)\n", debugstr_w(server), bufptr, jobid); return NetrJobAdd(server, (AT_INFO *)bufptr, jobid); } NET_API_STATUS WINAPI NetScheduleJobDel(LPCWSTR server, DWORD minjobid, DWORD maxjobid) { TRACE("(%s, %u, %u)\n", debugstr_w(server), minjobid, maxjobid); return NetrJobDel(server, minjobid, maxjobid); } NET_API_STATUS WINAPI NetScheduleJobEnum(LPCWSTR server, LPBYTE* bufptr, DWORD prefmaxsize, LPDWORD entriesread, LPDWORD totalentries, LPDWORD resumehandle) { AT_ENUM_CONTAINER container; NET_API_STATUS ret; TRACE("(%s, %p, %u, %p, %p, %p)\n", debugstr_w(server), bufptr, prefmaxsize, entriesread, totalentries, resumehandle); container.EntriesRead = 0; container.Buffer = NULL; ret = NetrJobEnum(server, &container, prefmaxsize, totalentries, resumehandle); if (ret == ERROR_SUCCESS) { *bufptr = (LPBYTE)container.Buffer; *entriesread = container.EntriesRead; } return ret; } NET_API_STATUS WINAPI NetScheduleJobGetInfo(LPCWSTR server, DWORD jobid, LPBYTE *bufptr) { TRACE("(%s, %u, %p)\n", debugstr_w(server), jobid, bufptr); return NetrJobGetInfo(server, jobid, (LPAT_INFO *)bufptr); } 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); MIDL_user_free(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) { static const WCHAR admins[] = {'A','d','m','i','n','i','s','t','r','a','t','o','r','s',0}; NET_API_STATUS status; 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 = ARRAY_SIZE(user); 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; } #define NS_MAXDNAME 1025 static DWORD get_dc_info(const WCHAR *domain, WCHAR *dc, WCHAR *ip) { static const WCHAR pfx[] = {'_','l','d','a','p','.','_','t','c','p','.','d','c','.','_','m','s','d','c','s','.',0}; WCHAR name[NS_MAXDNAME]; DWORD ret, size; DNS_RECORDW *rec; lstrcpyW(name, pfx); lstrcatW(name, domain); ret = DnsQuery_W(name, DNS_TYPE_SRV, DNS_QUERY_STANDARD, NULL, &rec, NULL); TRACE("DnsQuery_W(%s) => %d\n", wine_dbgstr_w(domain), ret); if (ret == ERROR_SUCCESS) { TRACE("target %s, port %d\n", wine_dbgstr_w(rec->Data.Srv.pNameTarget), rec->Data.Srv.wPort); lstrcpynW(dc, rec->Data.Srv.pNameTarget, NS_MAXDNAME); DnsRecordListFree(rec, DnsFreeRecordList); /* IPv4 */ ret = DnsQuery_W(dc, DNS_TYPE_A, DNS_QUERY_STANDARD, NULL, &rec, NULL); TRACE("DnsQuery_W(%s) => %d\n", wine_dbgstr_w(dc), ret); if (ret == ERROR_SUCCESS) { SOCKADDR_IN addr; addr.sin_family = AF_INET; addr.sin_port = 0; addr.sin_addr.s_addr = rec->Data.A.IpAddress; size = IP6_ADDRESS_STRING_LENGTH; ret = WSAAddressToStringW((SOCKADDR *)&addr, sizeof(addr), NULL, ip, &size); if (!ret) TRACE("WSAAddressToStringW => %d, %s\n", ret, wine_dbgstr_w(ip)); DnsRecordListFree(rec, DnsFreeRecordList); return ret; } /* IPv6 */ ret = DnsQuery_W(dc, DNS_TYPE_AAAA, DNS_QUERY_STANDARD, NULL, &rec, NULL); TRACE("DnsQuery_W(%s) => %d\n", wine_dbgstr_w(dc), ret); if (ret == ERROR_SUCCESS) { SOCKADDR_IN6 addr; addr.sin6_family = AF_INET6; addr.sin6_port = 0; addr.sin6_scope_id = 0; memcpy(addr.sin6_addr.s6_addr, &rec->Data.AAAA.Ip6Address, sizeof(rec->Data.AAAA.Ip6Address)); size = IP6_ADDRESS_STRING_LENGTH; ret = WSAAddressToStringW((SOCKADDR *)&addr, sizeof(addr), NULL, ip, &size); if (!ret) TRACE("WSAAddressToStringW => %d, %s\n", ret, wine_dbgstr_w(ip)); DnsRecordListFree(rec, DnsFreeRecordList); } } return ret; } DWORD WINAPI DsGetDcNameW(LPCWSTR computer, LPCWSTR domain, GUID *domain_guid, LPCWSTR site, ULONG flags, PDOMAIN_CONTROLLER_INFOW *dc_info) { static const WCHAR pfxW[] = {'\\','\\'}; static const WCHAR default_site_nameW[] = {'D','e','f','a','u','l','t','-','F','i','r','s','t','-','S','i','t','e','-','N','a','m','e',0}; NTSTATUS status; POLICY_DNS_DOMAIN_INFO *dns_domain_info = NULL; DOMAIN_CONTROLLER_INFOW *info; WCHAR dc[NS_MAXDNAME], ip[IP6_ADDRESS_STRING_LENGTH]; DWORD size; FIXME("(%s, %s, %s, %s, %08x, %p): semi-stub\n", debugstr_w(computer), debugstr_w(domain), debugstr_guid(domain_guid), debugstr_w(site), flags, dc_info); if (!dc_info) return ERROR_INVALID_PARAMETER; if (!domain) { LSA_OBJECT_ATTRIBUTES attrs; LSA_HANDLE lsa; memset(&attrs, 0, sizeof(attrs)); attrs.Length = sizeof(attrs); status = LsaOpenPolicy(NULL, &attrs, POLICY_VIEW_LOCAL_INFORMATION, &lsa); if (status) return LsaNtStatusToWinError(status); status = LsaQueryInformationPolicy(lsa, PolicyDnsDomainInformation, (void **)&dns_domain_info); LsaClose(lsa); if (status) return LsaNtStatusToWinError(status); domain = dns_domain_info->DnsDomainName.Buffer; } status = get_dc_info(domain, dc, ip); if (status) return status; size = sizeof(DOMAIN_CONTROLLER_INFOW) + lstrlenW(domain) * sizeof(WCHAR) + sizeof(pfxW) * 2 + (lstrlenW(dc) + 1 + lstrlenW(ip) + 1) * sizeof(WCHAR) + lstrlenW(domain) * sizeof(WCHAR) /* assume forest == domain */ + sizeof(default_site_nameW) * 2; status = NetApiBufferAllocate(size, (void **)&info); if (status != NERR_Success) { LsaFreeMemory(dns_domain_info); return ERROR_NOT_ENOUGH_MEMORY; } info->DomainControllerName = (WCHAR *)(info + 1); memcpy(info->DomainControllerName, pfxW, sizeof(pfxW)); lstrcpyW(info->DomainControllerName + 2, dc); info->DomainControllerAddress = (WCHAR *)((char *)info->DomainControllerName + (strlenW(info->DomainControllerName) + 1) * sizeof(WCHAR)); memcpy(info->DomainControllerAddress, pfxW, sizeof(pfxW)); lstrcpyW(info->DomainControllerAddress + 2, ip); info->DomainControllerAddressType = DS_INET_ADDRESS; info->DomainGuid = dns_domain_info ? dns_domain_info->DomainGuid : GUID_NULL /* FIXME */; info->DomainName = (WCHAR *)((char *)info->DomainControllerAddress + (strlenW(info->DomainControllerAddress) + 1) * sizeof(WCHAR)); lstrcpyW(info->DomainName, domain); info->DnsForestName = (WCHAR *)((char *)info->DomainName + (lstrlenW(info->DomainName) + 1) * sizeof(WCHAR)); lstrcpyW(info->DnsForestName, domain); info->DcSiteName = (WCHAR *)((char *)info->DnsForestName + (lstrlenW(info->DnsForestName) + 1) * sizeof(WCHAR)); lstrcpyW(info->DcSiteName, default_site_nameW); info->ClientSiteName = (WCHAR *)((char *)info->DcSiteName + sizeof(default_site_nameW)); lstrcpyW(info->ClientSiteName, default_site_nameW); info->Flags = DS_DNS_DOMAIN_FLAG | DS_DNS_FOREST_FLAG; LsaFreeMemory(dns_domain_info); *dc_info = info; return ERROR_SUCCESS; } 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 && !strncmpiW( 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 == ARRAY_SIZE(httpW) && !strncmpiW( http_path, httpW, len )) ssl = FALSE; else if (len == ARRAY_SIZE(httpsW) && !strncmpiW( 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 >= '0' && *p <= '9') { 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 += ARRAY_SIZE(davrootW); 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 += ARRAY_SIZE(davrootW); for (i = 0; i < len_path; i++) { if (path[i] == '/') *buf++ = '\\'; else *buf++ = path[i]; } *buf = 0; *buflen = len; return ERROR_SUCCESS; } /************************************************************ * DsEnumerateDomainTrustsA (NETAPI32.@) */ DWORD WINAPI DsEnumerateDomainTrustsA(LPSTR server, ULONG flags, PDS_DOMAIN_TRUSTSA* domains, PULONG count) { FIXME("(%s, 0x%04x, %p, %p): stub\n", debugstr_a(server), flags, domains, count); return ERROR_NO_LOGON_SERVERS; } /************************************************************ * DsEnumerateDomainTrustsW (NETAPI32.@) */ DWORD WINAPI DsEnumerateDomainTrustsW(LPWSTR server, ULONG flags, PDS_DOMAIN_TRUSTSW* domains, PULONG count) { FIXME("(%s, 0x%04x, %p, %p): stub\n", debugstr_w(server), flags, domains, count); return ERROR_NO_LOGON_SERVERS; } DECLSPEC_HIDDEN void __RPC_FAR *__RPC_USER MIDL_user_allocate(SIZE_T n) { return HeapAlloc(GetProcessHeap(), 0, n); } DECLSPEC_HIDDEN void __RPC_USER MIDL_user_free(void __RPC_FAR *p) { HeapFree(GetProcessHeap(), 0, p); } DECLSPEC_HIDDEN handle_t __RPC_USER ATSVC_HANDLE_bind(ATSVC_HANDLE str) { static unsigned char ncalrpc[] = "ncalrpc"; unsigned char *binding_str; handle_t rpc_handle = 0; if (RpcStringBindingComposeA(NULL, ncalrpc, NULL, NULL, NULL, &binding_str) == RPC_S_OK) { RpcBindingFromStringBindingA(binding_str, &rpc_handle); RpcStringFreeA(&binding_str); } return rpc_handle; } DECLSPEC_HIDDEN void __RPC_USER ATSVC_HANDLE_unbind(ATSVC_HANDLE ServerName, handle_t rpc_handle) { RpcBindingFree(&rpc_handle); }