/*
This file is part of GNUnet.
Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006 GNUnet e.V.
GNUnet is free software: you can redistribute it and/or modify it
under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License,
or (at your option) any later version.
GNUnet 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
Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
SPDX-License-Identifier: AGPL3.0-or-later
*/
/**
* @file util/win.c
* @brief Helper functions for MS Windows in C++
* @author Nils Durner
*/
#ifndef _WIN_C
#define _WIN_C
#include "winproc.h"
#include "platform.h"
#include "gnunet_crypto_lib.h"
#include "gnunet_common.h"
#include "gnunet_connection_lib.h"
#include
#ifndef INHERITED_ACE
#define INHERITED_ACE 0x10
#endif
int plibc_conv_to_win_path(const char *pszUnix, char *pszWindows);
#define _IP_ADAPTER_UNICAST_ADDRESS_HEAD \
union { \
struct { \
ULONG Length; \
DWORD Flags; \
}; \
};
#define _IP_ADAPTER_UNICAST_ADDRESS_BASE \
SOCKET_ADDRESS Address; \
IP_PREFIX_ORIGIN PrefixOrigin; \
IP_SUFFIX_ORIGIN SuffixOrigin; \
IP_DAD_STATE DadState; \
ULONG ValidLifetime; \
ULONG PreferredLifetime; \
ULONG LeaseLifetime;
#define _IP_ADAPTER_UNICAST_ADDRESS_ADD_VISTA \
UINT8 OnLinkPrefixLength;
#define _IP_ADAPTER_UNICAST_ADDRESS_DEFINE(suffix,addition) \
typedef struct _IP_ADAPTER_UNICAST_ADDRESS##suffix { \
_IP_ADAPTER_UNICAST_ADDRESS_HEAD \
struct _IP_ADAPTER_UNICAST_ADDRESS##suffix *Next; \
_IP_ADAPTER_UNICAST_ADDRESS_BASE \
addition \
} IP_ADAPTER_UNICAST_ADDRESS##suffix, *PIP_ADAPTER_UNICAST_ADDRESS##suffix;
/* _IP_ADAPTER_UNICAST_ADDRESS_DEFINE(,) defined in w32api headers */
_IP_ADAPTER_UNICAST_ADDRESS_DEFINE(_VISTA,_IP_ADAPTER_UNICAST_ADDRESS_ADD_VISTA)
#ifndef __MINGW64_VERSION_MAJOR
typedef struct _IP_ADAPTER_WINS_SERVER_ADDRESS {
union {
ULONGLONG Alignment;
struct {
ULONG Length;
DWORD Reserved;
};
};
struct _IP_ADAPTER_WINS_SERVER_ADDRESS *Next;
SOCKET_ADDRESS Address;
} IP_ADAPTER_WINS_SERVER_ADDRESS, *PIP_ADAPTER_WINS_SERVER_ADDRESS, *PIP_ADAPTER_WINS_SERVER_ADDRESS_LH;
typedef struct _IP_ADAPTER_GATEWAY_ADDRESS {
union {
ULONGLONG Alignment;
struct {
ULONG Length;
DWORD Reserved;
};
};
struct _IP_ADAPTER_GATEWAY_ADDRESS *Next;
SOCKET_ADDRESS Address;
} IP_ADAPTER_GATEWAY_ADDRESS, *PIP_ADAPTER_GATEWAY_ADDRESS, *PIP_ADAPTER_GATEWAY_ADDRESS_LH;
#endif
typedef UINT32 NET_IF_COMPARTMENT_ID;
typedef GUID NET_IF_NETWORK_GUID;
#ifndef __MINGW64_VERSION_MAJOR
typedef enum _NET_IF_CONNECTION_TYPE {
NET_IF_CONNECTION_DEDICATED = 1,
NET_IF_CONNECTION_PASSIVE,
NET_IF_CONNECTION_DEMAND,
NET_IF_CONNECTION_MAXIMUM
} NET_IF_CONNECTION_TYPE, *PNET_IF_CONNECTION_TYPE;
typedef enum {
TUNNEL_TYPE_NONE = 0,
TUNNEL_TYPE_OTHER,
TUNNEL_TYPE_DIRECT,
TUNNEL_TYPE_6TO4,
TUNNEL_TYPE_ISATAP,
TUNNEL_TYPE_TEREDO,
TUNNEL_TYPE_IPHTTPS
} TUNNEL_TYPE, *PTUNNEL_TYPE;
#endif
/*
A DUID consists of a two-octet type code represented in network byte
order, followed by a variable number of octets that make up the
actual identifier. A DUID can be no more than 128 octets long (not
including the type code).
*/
#define MAX_DHCPV6_DUID_LENGTH 130
#ifndef __MINGW64_VERSION_MAJOR
typedef union _NET_LUID {
ULONG64 Value;
struct {
ULONG64 Reserved :24;
ULONG64 NetLuidIndex :24;
ULONG64 IfType :16;
} Info;
} NET_LUID, *PNET_LUID, IF_LUID;
#define MAX_DNS_SUFFIX_STRING_LENGTH 246
typedef struct _IP_ADAPTER_DNS_SUFFIX {
struct _IP_ADAPTER_DNS_SUFFIX *Next;
WCHAR String[MAX_DNS_SUFFIX_STRING_LENGTH];
} IP_ADAPTER_DNS_SUFFIX, *PIP_ADAPTER_DNS_SUFFIX;
#endif
#define _IP_ADAPTER_ADDRESSES_HEAD \
union { \
ULONGLONG Alignment; \
struct { \
ULONG Length; \
DWORD IfIndex; \
}; \
};
#define _IP_ADAPTER_ADDRESSES_BASE \
PCHAR AdapterName; \
PIP_ADAPTER_UNICAST_ADDRESS FirstUnicastAddress; \
PIP_ADAPTER_ANYCAST_ADDRESS FirstAnycastAddress; \
PIP_ADAPTER_MULTICAST_ADDRESS FirstMulticastAddress; \
PIP_ADAPTER_DNS_SERVER_ADDRESS FirstDnsServerAddress; \
PWCHAR DnsSuffix; \
PWCHAR Description; \
PWCHAR FriendlyName; \
BYTE PhysicalAddress[MAX_ADAPTER_ADDRESS_LENGTH]; \
DWORD PhysicalAddressLength; \
DWORD Flags; \
DWORD Mtu; \
DWORD IfType; \
IF_OPER_STATUS OperStatus;
#define _IP_ADAPTER_ADDRESSES_ADD_XPSP1 \
DWORD Ipv6IfIndex; \
DWORD ZoneIndices[16]; \
PIP_ADAPTER_PREFIX FirstPrefix; \
#define _IP_ADAPTER_ADDRESSES_ADD_VISTA \
_IP_ADAPTER_ADDRESSES_ADD_XPSP1 \
ULONG64 TransmitLinkSpeed; \
ULONG64 ReceiveLinkSpeed; \
PIP_ADAPTER_WINS_SERVER_ADDRESS_LH FirstWinsServerAddress; \
PIP_ADAPTER_GATEWAY_ADDRESS_LH FirstGatewayAddress; \
ULONG Ipv4Metric; \
ULONG Ipv6Metric; \
IF_LUID Luid; \
SOCKET_ADDRESS Dhcpv4Server; \
NET_IF_COMPARTMENT_ID CompartmentId; \
NET_IF_NETWORK_GUID NetworkGuid; \
NET_IF_CONNECTION_TYPE ConnectionType; \
TUNNEL_TYPE TunnelType; \
SOCKET_ADDRESS Dhcpv6Server; \
BYTE Dhcpv6ClientDuid[MAX_DHCPV6_DUID_LENGTH]; \
ULONG Dhcpv6ClientDuidLength; \
ULONG Dhcpv6Iaid;
#define _IP_ADAPTER_ADDRESSES_ADD_2008_OR_VISTASP1 \
_IP_ADAPTER_ADDRESSES_ADD_VISTA \
PIP_ADAPTER_DNS_SUFFIX FirstDnsSuffix;
#define _IP_ADAPTER_ADDRESSES_DEFINE(suffix,addition) \
typedef struct _IP_ADAPTER_ADDRESSES##suffix { \
_IP_ADAPTER_ADDRESSES_HEAD \
struct _IP_ADAPTER_ADDRESSES##suffix *Next; \
_IP_ADAPTER_ADDRESSES_BASE \
addition \
} IP_ADAPTER_ADDRESSES##suffix, *PIP_ADAPTER_ADDRESSES##suffix;
/* _IP_ADAPTER_ADDRESSES_DEFINE(,) defined in w32api headers */
_IP_ADAPTER_ADDRESSES_DEFINE(_XPSP1,_IP_ADAPTER_ADDRESSES_ADD_XPSP1)
_IP_ADAPTER_ADDRESSES_DEFINE(_VISTA,_IP_ADAPTER_ADDRESSES_ADD_VISTA)
_IP_ADAPTER_ADDRESSES_DEFINE(_2008_OR_VISTASP1,_IP_ADAPTER_ADDRESSES_ADD_2008_OR_VISTASP1)
static int
EnumNICs_IPv6_get_ifs_count (SOCKET s)
{
DWORD dwret = 0, err;
int iret;
iret = WSAIoctl (s, SIO_ADDRESS_LIST_QUERY, NULL, 0, NULL, 0,
&dwret, NULL, NULL);
err = GetLastError ();
if (iret == SOCKET_ERROR && err == WSAEFAULT)
return dwret;
else if (iret == 0)
return 0;
return GNUNET_SYSERR;
}
static int
EnumNICs_IPv6_get_ifs (SOCKET s, SOCKET_ADDRESS_LIST *inf, int size)
{
int iret;
DWORD dwret = 0;
iret = WSAIoctl (s, SIO_ADDRESS_LIST_QUERY, NULL, 0, inf, size,
&dwret, NULL, NULL);
if (iret != 0 || dwret != size)
{
/* It's supposed to succeed! And size should be the same */
return GNUNET_SYSERR;
}
return GNUNET_OK;
}
#undef GNUNET_malloc
#define GNUNET_malloc(a) HeapAlloc(GetProcessHeap (), HEAP_ZERO_MEMORY | \
HEAP_GENERATE_EXCEPTIONS, a)
#undef GNUNET_free
#define GNUNET_free(a) HeapFree(GetProcessHeap (), 0, a)
#undef GNUNET_free_non_null
#define GNUNET_free_non_null(a) do { if ((a) != NULL) GNUNET_free(a); } while (0)
static int
EnumNICs_IPv4_get_ifs (SOCKET s, INTERFACE_INFO **inf, int *size)
{
int iret;
DWORD dwret = 0;
DWORD error;
INTERFACE_INFO *ii = NULL;
DWORD ii_size = sizeof (INTERFACE_INFO) * 15;
while (TRUE)
{
if (ii_size >= sizeof (INTERFACE_INFO) * 1000)
return GNUNET_SYSERR;
ii = (INTERFACE_INFO *) GNUNET_malloc (ii_size);
dwret = 0;
iret = WSAIoctl (s, SIO_GET_INTERFACE_LIST, NULL, 0, ii, ii_size,
&dwret, NULL, NULL);
error = GetLastError ();
if (iret == SOCKET_ERROR)
{
if (error == WSAEFAULT)
{
GNUNET_free (ii);
ii_size *= 2;
continue;
}
GNUNET_free (ii);
return GNUNET_SYSERR;
}
else
{
*inf = ii;
*size = dwret;
return GNUNET_OK;
}
}
return GNUNET_SYSERR;
}
int
EnumNICs2 (INTERFACE_INFO **ifs4, int *ifs4_len, SOCKET_ADDRESS_LIST **ifs6)
{
int result = 0;
SOCKET s4;
SOCKET s6;
int ifs4len = 0;
int ifs6len = 0;
INTERFACE_INFO *interfaces4 = NULL;
SOCKET_ADDRESS_LIST *interfaces6 = NULL;
SetLastError (0);
s4 = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
(void) GetLastError ();
SetLastError (0);
s6 = socket (AF_INET6, SOCK_STREAM, IPPROTO_TCP);
(void) GetLastError ();
if (s6 != INVALID_SOCKET)
{
ifs6len = EnumNICs_IPv6_get_ifs_count (s6);
if (ifs6len > 0)
{
interfaces6 = (SOCKET_ADDRESS_LIST *) GNUNET_malloc (ifs6len);
result = EnumNICs_IPv6_get_ifs (s6, interfaces6, ifs6len) || result;
}
closesocket (s6);
s6 = INVALID_SOCKET;
}
if (s4 != INVALID_SOCKET)
{
result = EnumNICs_IPv4_get_ifs (s4, &interfaces4, &ifs4len) || result;
closesocket (s4);
s4 = INVALID_SOCKET;
}
if (ifs6len + ifs4len == 0)
goto error;
if (!result)
{
*ifs4 = interfaces4;
*ifs4_len = ifs4len;
*ifs6 = interfaces6;
return GNUNET_OK;
}
error:
if (interfaces4 != NULL)
GNUNET_free (interfaces4);
if (interfaces6 != NULL)
GNUNET_free (interfaces6);
if (s4 != INVALID_SOCKET)
closesocket (s4);
if (s6 != INVALID_SOCKET)
closesocket (s6);
return GNUNET_SYSERR;
}
/**
* @returns #GNUNET_OK on success, #GNUNET_SYSERR on error
*/
int
EnumNICs3 (struct EnumNICs3_results **results, int *results_count)
{
DWORD dwRetVal = 0;
int count = 0;
ULONG flags = /*GAA_FLAG_INCLUDE_PREFIX |*/ GAA_FLAG_SKIP_ANYCAST |
GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER;
struct sockaddr_in6 examplecom6;
IPAddr examplecom;
DWORD best_interface = 0;
DWORD best_interface6 = 0;
int use_enum2 = 0;
INTERFACE_INFO *interfaces4 = NULL;
int interfaces4_len = 0;
SOCKET_ADDRESS_LIST *interfaces6 = NULL;
unsigned long outBufLen = sizeof (IP_ADAPTER_ADDRESSES);
IP_ADAPTER_ADDRESSES *pCurrentAddress = NULL;
IP_ADAPTER_ADDRESSES *pAddresses = (IP_ADAPTER_ADDRESSES *) GNUNET_malloc (outBufLen);
if (GetAdaptersAddresses (AF_UNSPEC, flags, NULL, pAddresses, &outBufLen)
== ERROR_BUFFER_OVERFLOW)
{
GNUNET_free (pAddresses);
pAddresses = (IP_ADAPTER_ADDRESSES *) GNUNET_malloc (outBufLen);
}
dwRetVal = GetAdaptersAddresses (AF_UNSPEC, flags, NULL, pAddresses, &outBufLen);
if (dwRetVal != NO_ERROR)
{
GNUNET_free (pAddresses);
return GNUNET_SYSERR;
}
if (pAddresses->Length < sizeof (IP_ADAPTER_ADDRESSES_VISTA))
{
use_enum2 = 1;
/* Enumerate NICs using WSAIoctl() */
if (GNUNET_OK != EnumNICs2 (&interfaces4, &interfaces4_len, &interfaces6))
{
GNUNET_free (pAddresses);
return GNUNET_SYSERR;
}
}
examplecom = inet_addr("192.0.34.166"); /* www.example.com */
if (GetBestInterface (examplecom, &best_interface) != NO_ERROR)
best_interface = 0;
if (GNGetBestInterfaceEx != NULL)
{
examplecom6.sin6_family = AF_INET6;
examplecom6.sin6_port = 0;
examplecom6.sin6_flowinfo = 0;
examplecom6.sin6_scope_id = 0;
inet_pton (AF_INET6, "2001:500:88:200:0:0:0:10",
(struct sockaddr *) &examplecom6.sin6_addr);
dwRetVal = GNGetBestInterfaceEx ((struct sockaddr *) &examplecom6,
&best_interface6);
if (dwRetVal != NO_ERROR)
best_interface6 = 0;
}
/* Give IPv6 a priority */
if (best_interface6 != 0)
best_interface = best_interface6;
count = 0;
for (pCurrentAddress = pAddresses;
pCurrentAddress != NULL; pCurrentAddress = pCurrentAddress->Next)
{
if (pCurrentAddress->OperStatus == IfOperStatusUp)
{
IP_ADAPTER_UNICAST_ADDRESS *unicast = NULL;
for (unicast = pCurrentAddress->FirstUnicastAddress; unicast != NULL;
unicast = unicast->Next)
{
if ((unicast->Address.lpSockaddr->sa_family == AF_INET ||
unicast->Address.lpSockaddr->sa_family == AF_INET6) &&
(unicast->DadState == IpDadStateDeprecated ||
unicast->DadState == IpDadStatePreferred))
count += 1;
}
}
}
if (count == 0)
{
*results = NULL;
*results_count = 0;
GNUNET_free (pAddresses);
GNUNET_free_non_null (interfaces4);
GNUNET_free_non_null (interfaces6);
return GNUNET_OK;
}
*results = (struct EnumNICs3_results *) GNUNET_malloc (
sizeof (struct EnumNICs3_results) * count);
*results_count = count;
count = 0;
for (pCurrentAddress = pAddresses;
pCurrentAddress != NULL; pCurrentAddress = pCurrentAddress->Next)
{
struct EnumNICs3_results *r;
IP_ADAPTER_UNICAST_ADDRESS *unicast = NULL;
if (pCurrentAddress->OperStatus != IfOperStatusUp)
continue;
for (unicast = pCurrentAddress->FirstUnicastAddress; unicast != NULL;
unicast = unicast->Next)
{
int i, j;
int mask_length = -1;
char dst[INET6_ADDRSTRLEN + 1];
if ((unicast->Address.lpSockaddr->sa_family != AF_INET &&
unicast->Address.lpSockaddr->sa_family != AF_INET6) ||
(unicast->DadState != IpDadStateDeprecated &&
unicast->DadState != IpDadStatePreferred))
continue;
r = &(*results)[count];
r->flags = 0;
if (pCurrentAddress->IfIndex > 0 &&
pCurrentAddress->IfIndex == best_interface &&
unicast->Address.lpSockaddr->sa_family == AF_INET)
r->is_default = 1;
else if (pCurrentAddress->Ipv6IfIndex > 0 &&
pCurrentAddress->Ipv6IfIndex == best_interface6 &&
unicast->Address.lpSockaddr->sa_family == AF_INET6)
r->is_default = 1;
else
r->is_default = 0;
/* Don't choose default interface twice */
if (r->is_default)
best_interface = best_interface6 = 0;
if (!use_enum2)
{
GNUNET_memcpy (&r->address, unicast->Address.lpSockaddr,
unicast->Address.iSockaddrLength);
memset (&r->mask, 0, sizeof (struct sockaddr));
mask_length = ((IP_ADAPTER_UNICAST_ADDRESS_VISTA *) unicast)->
OnLinkPrefixLength;
/* OnLinkPrefixLength is the number of leading 1s in the mask.
* OnLinkPrefixLength is available on Vista and later (hence use_enum2).
*/
if (unicast->Address.lpSockaddr->sa_family == AF_INET)
{
struct sockaddr_in *m = (struct sockaddr_in *) &r->mask;
for (i = 0; i < mask_length; i++)
((unsigned char *) &m->sin_addr)[i / 8] |= 0x80 >> (i % 8);
}
else if (unicast->Address.lpSockaddr->sa_family == AF_INET6)
{
struct sockaddr_in6 *m = (struct sockaddr_in6 *) &r->mask;
struct sockaddr_in6 *b = (struct sockaddr_in6 *) &r->broadcast;
for (i = 0; i < mask_length; i++)
((unsigned char *) &m->sin6_addr)[i / 8] |= 0x80 >> (i % 8);
GNUNET_memcpy (&r->broadcast, &r->address, unicast->Address.iSockaddrLength);
for (i = mask_length; i < 128; i++)
((unsigned char *) &b->sin6_addr)[i / 8] |= 0x80 >> (i % 8);
}
r->flags |= ENUMNICS3_MASK_OK;
}
else
{
int found = 0;
if (unicast->Address.lpSockaddr->sa_family == AF_INET)
{
for (i = 0; !found && i < interfaces4_len / sizeof (INTERFACE_INFO); i++)
{
struct sockaddr_in *m = (struct sockaddr_in *) &r->mask;
GNUNET_memcpy (&interfaces4[i].iiAddress.Address,
unicast->Address.lpSockaddr,
unicast->Address.iSockaddrLength);
found = 1;
GNUNET_memcpy (&r->address, &interfaces4[i].iiAddress.Address,
sizeof (struct sockaddr_in));
GNUNET_memcpy (&r->mask, &interfaces4[i].iiNetmask.Address,
sizeof (struct sockaddr_in));
for (mask_length = 0;
((unsigned char *) &m->sin_addr)[mask_length / 8] &
0x80 >> (mask_length % 8); mask_length++)
{
}
r->flags |= ENUMNICS3_MASK_OK;
}
}
else if (unicast->Address.lpSockaddr->sa_family == AF_INET6)
{
for (i = 0;
interfaces6 != NULL && !found && i < interfaces6->iAddressCount;
i++)
{
GNUNET_memcpy (interfaces6->Address[i].lpSockaddr,
unicast->Address.lpSockaddr,
unicast->Address.iSockaddrLength);
found = 1;
GNUNET_memcpy (&r->address, interfaces6->Address[i].lpSockaddr,
sizeof (struct sockaddr_in6));
/* TODO: Find a way to reliably get network mask for IPv6 on XP */
memset (&r->mask, 0, sizeof (struct sockaddr));
r->flags &= ~ENUMNICS3_MASK_OK;
}
}
if (!found)
{
DebugBreak ();
}
}
if (unicast->Address.lpSockaddr->sa_family == AF_INET)
{
struct sockaddr_in *m = (struct sockaddr_in *) &r->mask;
struct sockaddr_in *a = (struct sockaddr_in *) &r->address;
/* copy address to broadcast, then flip all the trailing bits not
* falling under netmask to 1,
* so we get, 192.168.0.255 from, say, 192.168.0.43 with mask == 24.
*/
GNUNET_memcpy (&r->broadcast, &r->address, unicast->Address.iSockaddrLength);
for (i = mask_length; i < 32; i++)
((unsigned char *) &m->sin_addr)[i / 8] |= 0x80 >> (i % 8);
r->flags |= ENUMNICS3_BCAST_OK;
r->addr_size = sizeof (struct sockaddr_in);
inet_ntop (AF_INET, &a->sin_addr, dst, INET_ADDRSTRLEN);
}
else if (unicast->Address.lpSockaddr->sa_family == AF_INET6)
{
struct sockaddr_in6 *a = (struct sockaddr_in6 *) &r->address;
/* for IPv6 broadcast is not defined, zero it down */
memset (&r->broadcast, 0, sizeof (struct sockaddr));
r->flags &= ~ENUMNICS3_BCAST_OK;
r->addr_size = sizeof (struct sockaddr_in6);
inet_ntop (AF_INET6, &a->sin6_addr, dst, INET6_ADDRSTRLEN);
}
i = 0;
i += snprintf (&r->pretty_name[i], 1000 - i > 0 ? 1000 - i : 0,
"%S (%s", pCurrentAddress->FriendlyName, dst);
for (j = 0; j < pCurrentAddress->PhysicalAddressLength; j++)
i += snprintf (&r->pretty_name[i], 1000 - i > 0 ? 1000 - i : 0,
"%s%02X",j > 0 ? ":" : " - ", pCurrentAddress->PhysicalAddress[j]);
i += snprintf (&r->pretty_name[i], 1000 - i > 0 ? 1000 - i : 0, ")");
r->pretty_name[1000] = '\0';
count += 1;
}
}
if (use_enum2)
{
GNUNET_free_non_null (interfaces4);
GNUNET_free_non_null (interfaces6);
}
GNUNET_free (pAddresses);
return GNUNET_OK;
}
void
EnumNICs3_free (struct EnumNICs3_results *r)
{
GNUNET_free_non_null (r);
}
/**
* Lists all network interfaces in a combo box
* Used by the basic GTK configurator
*
* @param callback function to call for each NIC
* @param callback_cls closure for callback
*/
int
ListNICs (void (*callback) (void *, const char *, int), void * callback_cls)
{
int r;
int i;
struct EnumNICs3_results *results = NULL;
int results_count;
r = EnumNICs3 (&results, &results_count);
if (r != GNUNET_OK)
return GNUNET_NO;
for (i = 0; i < results_count; i++)
callback (callback_cls, results[i].pretty_name, results[i].is_default);
GNUNET_free_non_null (results);
return GNUNET_YES;
}
/**
* @brief Installs the Windows service
* @param servicename name of the service as diplayed by the SCM
* @param application path to the application binary
* @param username the name of the service's user account
* @returns 0 on success
* 1 if the Windows version doesn't support services
* 2 if the SCM could not be opened
* 3 if the service could not be created
*/
int InstallAsService(char *servicename, char *application, char *username)
{
SC_HANDLE hManager, hService;
char szEXE[_MAX_PATH + 17] = "\"";
char *user = NULL;
if (! GNOpenSCManager)
return 1;
plibc_conv_to_win_path(application, szEXE + 1);
strcat(szEXE, "\" --win-service");
hManager = GNOpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
if (! hManager)
return 2;
if (username)
{
user = (char *) malloc(strlen(username) + 3);
sprintf(user, ".\\%s", username);
}
hService = GNCreateService(hManager, (LPCTSTR) servicename, (LPCTSTR) servicename, 0,
SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, (LPCTSTR) szEXE,
NULL, NULL, NULL, (LPCTSTR) user, (LPCTSTR) username);
if (user)
free(user);
if (! hService)
return 3;
GNCloseServiceHandle(hService);
return 0;
}
/**
* @brief Uninstall Windows service
* @param servicename name of the service to delete
* @returns 0 on success
* 1 if the Windows version doesn't support services
* 2 if the SCM could not be openend
* 3 if the service cannot be accessed
* 4 if the service cannot be deleted
*/
int
UninstallService(char *servicename)
{
SC_HANDLE hManager, hService;
if (! GNOpenSCManager)
return 1;
hManager = GNOpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
if (! hManager)
return 2;
if (! (hService = GNOpenService(hManager, (LPCTSTR) servicename, DELETE)))
{
if (GetLastError() != ERROR_SERVICE_DOES_NOT_EXIST)
return 3;
else
goto closeSCM;
}
if (! GNDeleteService(hService))
if (GetLastError() != ERROR_SERVICE_MARKED_FOR_DELETE)
return 4;
closeSCM:
GNCloseServiceHandle(hService);
return 0;
}
/**
* @author Scott Field, Microsoft
* @see http://support.microsoft.com/?scid=kb;en-us;132958
* @date 12-Jul-95
*/
void
_InitLsaString(PLSA_UNICODE_STRING LsaString, LPWSTR String)
{
DWORD StringLength;
if(String == NULL)
{
LsaString->Buffer = NULL;
LsaString->Length = 0;
LsaString->MaximumLength = 0;
return;
}
StringLength = wcslen(String);
LsaString->Buffer = String;
LsaString->Length = (USHORT) StringLength *sizeof(WCHAR);
LsaString->MaximumLength = (USHORT) (StringLength + 1) * sizeof(WCHAR);
}
/**
* @author Scott Field, Microsoft
* @see http://support.microsoft.com/?scid=kb;en-us;132958
* @date 12-Jul-95
*/
NTSTATUS
_OpenPolicy(LPWSTR ServerName, DWORD DesiredAccess, PLSA_HANDLE PolicyHandle)
{
LSA_OBJECT_ATTRIBUTES ObjectAttributes;
LSA_UNICODE_STRING ServerString;
PLSA_UNICODE_STRING Server = NULL;
/* Always initialize the object attributes to all zeroes. */
ZeroMemory(&ObjectAttributes, sizeof(ObjectAttributes));
if(ServerName != NULL)
{
/* Make a LSA_UNICODE_STRING out of the LPWSTR passed in */
_InitLsaString(&ServerString, ServerName);
Server = &ServerString;
}
/* Attempt to open the policy. */
return GNLsaOpenPolicy(Server,
&ObjectAttributes, DesiredAccess, PolicyHandle);
}
/**
* @brief Obtain a SID representing the supplied account on the supplied system
* @return TRUE on success, FALSE on failure
* @author Scott Field, Microsoft
* @date 12-Jul-95
* @remarks A buffer is allocated which contains the SID representing the
* supplied account. This buffer should be freed when it is no longer
* needed by calling\n
* HeapFree(GetProcessHeap(), 0, buffer)
* @remarks Call GetLastError() to obtain extended error information.
* @see http://support.microsoft.com/?scid=kb;en-us;132958
*/
BOOL
_GetAccountSid(LPCTSTR SystemName, LPCTSTR AccountName, PSID * Sid)
{
LPTSTR ReferencedDomain = NULL;
DWORD cbSid = 128; /* initial allocation attempt */
DWORD cchReferencedDomain = 16; /* initial allocation size */
SID_NAME_USE peUse;
BOOL bSuccess = FALSE; /* assume this function will fail */
/* initial memory allocations */
if ((*Sid = HeapAlloc (GetProcessHeap (), 0, cbSid)) == NULL)
return FALSE;
if ((ReferencedDomain = (LPTSTR) HeapAlloc (GetProcessHeap (),
0,
cchReferencedDomain *
sizeof (TCHAR))) == NULL)
return FALSE;
/* Obtain the SID of the specified account on the specified system. */
while (!GNLookupAccountName(SystemName, /* machine to lookup account on */
AccountName, /* account to lookup */
*Sid, /* SID of interest */
&cbSid, /* size of SID */
ReferencedDomain, /* domain account was found on */
&cchReferencedDomain, &peUse))
{
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
/* reallocate memory */
if ((*Sid = HeapReAlloc (GetProcessHeap (), 0, *Sid, cbSid)) == NULL)
return FALSE;
if ((ReferencedDomain = (LPTSTR) HeapReAlloc (GetProcessHeap (),
0,
ReferencedDomain,
cchReferencedDomain
* sizeof (TCHAR))) == NULL)
return FALSE;
}
else
goto end;
}
/* Indicate success. */
bSuccess = TRUE;
end:
/* Cleanup and indicate failure, if appropriate. */
HeapFree (GetProcessHeap (), 0, ReferencedDomain);
if (!bSuccess)
{
if (*Sid != NULL)
{
HeapFree (GetProcessHeap (), 0, *Sid);
*Sid = NULL;
}
}
return bSuccess;
}
/**
* @author Scott Field, Microsoft
* @see http://support.microsoft.com/?scid=kb;en-us;132958
* @date 12-Jul-95
*/
NTSTATUS
_SetPrivilegeOnAccount(LSA_HANDLE PolicyHandle,/* open policy handle */
PSID AccountSid, /* SID to grant privilege to */
LPWSTR PrivilegeName, /* privilege to grant (Unicode) */
BOOL bEnable /* enable or disable */
)
{
LSA_UNICODE_STRING PrivilegeString;
/* Create a LSA_UNICODE_STRING for the privilege name. */
_InitLsaString(&PrivilegeString, PrivilegeName);
/* grant or revoke the privilege, accordingly */
if(bEnable)
{
NTSTATUS i;
i = GNLsaAddAccountRights(PolicyHandle, /* open policy handle */
AccountSid, /* target SID */
&PrivilegeString, /* privileges */
1 /* privilege count */
);
return i;
}
else
{
return GNLsaRemoveAccountRights(PolicyHandle, /* open policy handle */
AccountSid, /* target SID */
FALSE, /* do not disable all rights */
&PrivilegeString, /* privileges */
1 /* privilege count */
);
}
}
/**
* @brief Create a Windows service account
* @return 0 on success, > 0 otherwise
* @param pszName the name of the account
* @param pszDesc description of the account
*/
int
CreateServiceAccount(const char *pszName,
const char *pszDesc)
{
USER_INFO_1 ui;
USER_INFO_1008 ui2;
NET_API_STATUS nStatus;
wchar_t wszName[MAX_NAME_LENGTH], wszDesc[MAX_NAME_LENGTH];
LSA_HANDLE hPolicy;
PSID pSID;
if (! GNNetUserAdd)
return 1;
mbstowcs(wszName, pszName, strlen(pszName) + 1);
mbstowcs(wszDesc, pszDesc, strlen(pszDesc) + 1);
memset(&ui, 0, sizeof(ui));
ui.usri1_name = wszName;
ui.usri1_password = wszName; /* account is locked anyway */
ui.usri1_priv = USER_PRIV_USER;
ui.usri1_comment = wszDesc;
ui.usri1_flags = UF_SCRIPT;
nStatus = GNNetUserAdd(NULL, 1, (LPBYTE)&ui, NULL);
if (nStatus != NERR_Success && nStatus != NERR_UserExists)
return 2;
ui2.usri1008_flags = UF_PASSWD_CANT_CHANGE | UF_DONT_EXPIRE_PASSWD;
GNNetUserSetInfo(NULL, wszName, 1008, (LPBYTE)&ui2, NULL);
if (!NT_SUCCESS(_OpenPolicy(NULL, POLICY_ALL_ACCESS, &hPolicy)))
return 3;
_GetAccountSid(NULL, (LPCTSTR) pszName, &pSID);
if (!NT_SUCCESS(_SetPrivilegeOnAccount(hPolicy, pSID, (LPWSTR) L"SeServiceLogonRight", TRUE)))
return 4;
_SetPrivilegeOnAccount(hPolicy, pSID, (LPWSTR) L"SeDenyInteractiveLogonRight", TRUE);
_SetPrivilegeOnAccount(hPolicy, pSID, (LPWSTR) L"SeDenyBatchLogonRight", TRUE);
_SetPrivilegeOnAccount(hPolicy, pSID, (LPWSTR) L"SeDenyNetworkLogonRight", TRUE);
GNLsaClose(hPolicy);
return 0;
}
/**
* @brief Grant permission to a file
* @param lpszFileName the name of the file or directory
* @param lpszAccountName the user account
* @param dwAccessMask the desired access (e.g. GENERIC_ALL)
* @return TRUE on success
* @remark based on http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q102102&
*/
BOOL AddPathAccessRights(char *lpszFileName, char *lpszAccountName,
DWORD dwAccessMask)
{
/* SID variables. */
SID_NAME_USE snuType;
TCHAR * szDomain = NULL;
DWORD cbDomain = 0;
LPVOID pUserSID = NULL;
DWORD cbUserSID = 0;
/* File SD variables. */
PSECURITY_DESCRIPTOR pFileSD = NULL;
DWORD cbFileSD = 0;
/* New SD variables. */
SECURITY_DESCRIPTOR newSD;
/* ACL variables. */
PACL pACL = NULL;
BOOL fDaclPresent;
BOOL fDaclDefaulted;
ACL_SIZE_INFORMATION AclInfo;
/* New ACL variables. */
PACL pNewACL = NULL;
DWORD cbNewACL = 0;
/* Temporary ACE. */
LPVOID pTempAce = NULL;
UINT CurrentAceIndex = 0;
UINT newAceIndex = 0;
/* Assume function will fail. */
BOOL fResult = FALSE;
BOOL fAPISuccess;
SECURITY_INFORMATION secInfo = DACL_SECURITY_INFORMATION;
/**
* STEP 1: Get SID of the account name specified.
*/
fAPISuccess = GNLookupAccountName(NULL, (LPCTSTR) lpszAccountName,
pUserSID, &cbUserSID, (LPTSTR) szDomain, &cbDomain, &snuType);
/* API should have failed with insufficient buffer. */
if (fAPISuccess)
goto end;
else if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
goto end;
}
pUserSID = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbUserSID);
if (!pUserSID) {
goto end;
}
szDomain = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbDomain * sizeof(TCHAR));
if (!szDomain) {
goto end;
}
fAPISuccess = GNLookupAccountName(NULL, (LPCTSTR) lpszAccountName,
pUserSID, &cbUserSID, (LPTSTR) szDomain, &cbDomain, &snuType);
if (!fAPISuccess) {
goto end;
}
/**
* STEP 2: Get security descriptor (SD) of the file specified.
*/
fAPISuccess = GNGetFileSecurity((LPCTSTR) lpszFileName,
secInfo, pFileSD, 0, &cbFileSD);
/* API should have failed with insufficient buffer. */
if (fAPISuccess)
goto end;
else if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
goto end;
}
pFileSD = (PSECURITY_DESCRIPTOR) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
cbFileSD);
if (!pFileSD) {
goto end;
}
fAPISuccess = GNGetFileSecurity((LPCTSTR) lpszFileName,
secInfo, pFileSD, cbFileSD, &cbFileSD);
if (!fAPISuccess) {
goto end;
}
/**
* STEP 3: Initialize new SD.
*/
if (!GNInitializeSecurityDescriptor(&newSD,
SECURITY_DESCRIPTOR_REVISION)) {
goto end;
}
/**
* STEP 4: Get DACL from the old SD.
*/
if (!GNGetSecurityDescriptorDacl(pFileSD, &fDaclPresent, &pACL,
&fDaclDefaulted)) {
goto end;
}
/**
* STEP 5: Get size information for DACL.
*/
AclInfo.AceCount = 0; // Assume NULL DACL.
AclInfo.AclBytesFree = 0;
AclInfo.AclBytesInUse = sizeof(ACL);
if (pACL == NULL)
fDaclPresent = FALSE;
/* If not NULL DACL, gather size information from DACL. */
if (fDaclPresent) {
if (!GNGetAclInformation(pACL, &AclInfo,
sizeof(ACL_SIZE_INFORMATION), AclSizeInformation)) {
goto end;
}
}
/**
* STEP 6: Compute size needed for the new ACL.
*/
cbNewACL = AclInfo.AclBytesInUse + sizeof(ACCESS_ALLOWED_ACE)
+ GetLengthSid(pUserSID) - sizeof(DWORD);
/**
* STEP 7: Allocate memory for new ACL.
*/
pNewACL = (PACL) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbNewACL);
if (!pNewACL) {
goto end;
}
/**
* STEP 8: Initialize the new ACL.
*/
if (!GNInitializeAcl(pNewACL, cbNewACL, ACL_REVISION2)) {
goto end;
}
/**
* STEP 9 If DACL is present, copy all the ACEs from the old DACL
* to the new DACL.
*
* The following code assumes that the old DACL is
* already in Windows 2000 preferred order. To conform
* to the new Windows 2000 preferred order, first we will
* copy all non-inherited ACEs from the old DACL to the
* new DACL, irrespective of the ACE type.
*/
newAceIndex = 0;
if (fDaclPresent && AclInfo.AceCount) {
for (CurrentAceIndex = 0;
CurrentAceIndex < AclInfo.AceCount;
CurrentAceIndex++) {
/**
* TEP 10: Get an ACE.
*/
if (!GNGetAce(pACL, CurrentAceIndex, &pTempAce)) {
goto end;
}
/**
* STEP 11: Check if it is a non-inherited ACE.
* If it is an inherited ACE, break from the loop so
* that the new access allowed non-inherited ACE can
* be added in the correct position, immediately after
* all non-inherited ACEs.
*/
if (((ACCESS_ALLOWED_ACE *)pTempAce)->Header.AceFlags
& INHERITED_ACE)
break;
/**
* STEP 12: Skip adding the ACE, if the SID matches
* with the account specified, as we are going to
* add an access allowed ACE with a different access
* mask.
*/
if (GNEqualSid(pUserSID,
&(((ACCESS_ALLOWED_ACE *)pTempAce)->SidStart)))
continue;
/**
* STEP 13: Add the ACE to the new ACL.
*/
if (!GNAddAce(pNewACL, ACL_REVISION, MAXDWORD, pTempAce,
((PACE_HEADER) pTempAce)->AceSize)) {
goto end;
}
newAceIndex++;
}
}
/**
* STEP 14: Add the access-allowed ACE to the new DACL.
* The new ACE added here will be in the correct position,
* immediately after all existing non-inherited ACEs.
*/
if (!GNAddAccessAllowedAce(pNewACL, ACL_REVISION2, dwAccessMask,
pUserSID)) {
goto end;
}
/**
* STEP 14.5: Make new ACE inheritable
*/
if (!GetAce(pNewACL, newAceIndex, &pTempAce))
goto end;
((ACCESS_ALLOWED_ACE *)pTempAce)->Header.AceFlags |=
(CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE);
/**
* STEP 15: To conform to the new Windows 2000 preferred order,
* we will now copy the rest of inherited ACEs from the
* old DACL to the new DACL.
*/
if (fDaclPresent && AclInfo.AceCount) {
for (;
CurrentAceIndex < AclInfo.AceCount;
CurrentAceIndex++) {
/**
* STEP 16: Get an ACE.
*/
if (!GNGetAce(pACL, CurrentAceIndex, &pTempAce)) {
goto end;
}
/**
* STEP 17: Add the ACE to the new ACL.
*/
if (!GNAddAce(pNewACL, ACL_REVISION, MAXDWORD, pTempAce,
((PACE_HEADER) pTempAce)->AceSize)) {
goto end;
}
}
}
/**
* STEP 18: Set permissions
*/
if (GNSetNamedSecurityInfo((LPTSTR) lpszFileName, SE_FILE_OBJECT,
DACL_SECURITY_INFORMATION, NULL, NULL, pNewACL, NULL) != ERROR_SUCCESS) {
goto end;
}
fResult = TRUE;
end:
/**
* STEP 19: Free allocated memory
*/
if (pUserSID)
HeapFree(GetProcessHeap(), 0, pUserSID);
if (szDomain)
HeapFree(GetProcessHeap(), 0, szDomain);
if (pFileSD)
HeapFree(GetProcessHeap(), 0, pFileSD);
if (pNewACL)
HeapFree(GetProcessHeap(), 0, pNewACL);
return fResult;
}
char *winErrorStr(const char *prefix, int dwErr)
{
char *err, *ret;
int mem;
if (! FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL, (DWORD) dwErr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &err,
0, NULL ))
{
err = (char *) LocalAlloc (LMEM_FIXED | LMEM_ZEROINIT, 1);
}
mem = strlen(err) + strlen(prefix) + 20;
ret = (char *) malloc(mem);
snprintf(ret, mem, "%s: %s (#%u)", prefix, err, dwErr);
LocalFree(err);
return ret;
}
/**
* Terminate a process by creating a remote thread within it,
* which proceeds to call ExitProcess() inside that process.
* Safer than TerminateProcess ().
*
* Code is from From http://private-storm.de/2009/08/11/case-terminateprocess/
*
* @param hProcess handle of a process to terminate
* @param uExitCode exit code to use for ExitProcess()
* @param dwTimeout number of ms to wait for the process to terminate
* @return TRUE on success, FALSE on failure (check last error for the code)
*/
BOOL
SafeTerminateProcess (HANDLE hProcess, UINT uExitCode, DWORD dwTimeout)
{
DWORD dwTID, dwCode, dwErr = 0;
HANDLE hProcessDup = INVALID_HANDLE_VALUE;
HANDLE hRT = NULL;
HINSTANCE hKernel = GetModuleHandle ("Kernel32");
BOOL bSuccess = FALSE;
BOOL bDup = DuplicateHandle (GetCurrentProcess (), hProcess,
GetCurrentProcess (), &hProcessDup, PROCESS_ALL_ACCESS,
FALSE, 0);
/* Detect the special case where the process is
* already dead...
*/
if (GetExitCodeProcess (bDup ? hProcessDup : hProcess, &dwCode) &&
(STILL_ACTIVE == dwCode))
{
FARPROC pfnExitProc;
pfnExitProc = GetProcAddress (hKernel, "ExitProcess");
hRT = CreateRemoteThread ((bDup) ? hProcessDup : hProcess, NULL, 0,
(LPTHREAD_START_ROUTINE) pfnExitProc, (PVOID) uExitCode, 0, &dwTID);
dwErr = GetLastError ();
}
else
{
dwErr = ERROR_PROCESS_ABORTED;
}
if (hRT)
{
/* Must wait process to terminate to
* guarantee that it has exited...
*/
DWORD dwWaitResult = WaitForSingleObject ((bDup) ? hProcessDup : hProcess,
dwTimeout);
if (dwWaitResult == WAIT_TIMEOUT)
dwErr = WAIT_TIMEOUT;
else
dwErr = GetLastError ();
CloseHandle (hRT);
bSuccess = dwErr == NO_ERROR;
}
if (bDup)
CloseHandle (hProcessDup);
SetLastError (dwErr);
return bSuccess;
}
#endif