Best way to get the connected network domain name in Windows - windows

I would like to find out whether the local Windows system is connected to a network domain (instead of a workgroup) and if so, read the domain's name.
I found these Windows API functions to achieve this:
GetEnvironmentVariable('USERDNSDOMAIN')
NetGetJoinInformation
NetServerGetInfo
NetWkstaGetInfo
LookupAccountSid
Are there any advantages or disadvantages between them? (faster, more reliable, more accurate, ...)
Which one would you recommend and why?

LookupAccountSid is more focused on searching for sid, NetServerGetInfo is focused on retrieving server information.
So neither of these applies to you.
The domain name gets from NetGetJoinInformation and NetWkstaGetInfo correspond to USERDOMAIN instead of USERDNSDOMAIN, Depending on the domain name you want.
GetEnvironmentVariable is the function that just get the value of a variable and can be modified by SetEnvironmentVariable at any time (Even though we usually don't do this), so I don't recommend it.
No special group membership is required to successfully execute the
NetGetJoinInformation function.
And it is more pure than NetWkstaGetInfo(according to your requirement)

most direct and efficient here - call LsaQueryInformationPolicy with PolicyDnsDomainInformation. on output you got filled POLICY_DNS_DOMAIN_INFO structure. here will be name and DNS name of the primary domain. and also it SID
If the computer associated with the Policy object is not a member of a
domain, all structure members except Name are NULL or zero.
#include <Ntsecapi.h>
NTSTATUS PrintDomainName()
{
LSA_HANDLE PolicyHandle;
static LSA_OBJECT_ATTRIBUTES oa = { sizeof(oa) };
NTSTATUS status = LsaOpenPolicy(0, &oa, POLICY_VIEW_LOCAL_INFORMATION, &PolicyHandle);
if (LSA_SUCCESS(status))
{
PPOLICY_DNS_DOMAIN_INFO ppddi;
if (LSA_SUCCESS(status = LsaQueryInformationPolicy(PolicyHandle, PolicyDnsDomainInformation, (void**)&ppddi)))
{
if (ppddi->Sid)
{
DbgPrint("DnsDomainName: %wZ\n", &ppddi->DnsDomainName);
}
else
{
DbgPrint("%wZ: not a member of a domain\n", &ppddi->Name);
}
LsaFreeMemory(ppddi);
}
LsaClose(PolicyHandle);
}
return status;
}
the NetGetJoinInformation internally do the same - query PolicyDnsDomainInformation, but do this not in your but in remote process (svchost.exe -k networkservice -p -s LanmanWorkstation - LanmanWorkstation service) and many additional calls do. so it less efficient, but and less source code for call this api

Related

How would you explain this type of syntax (casting)?

Looking at the source code c.Organizations = (*OrganizationsService)(&c.common) from https://github.com/google/go-github/blob/master/github/github.go#L283, in describing this bit of code with proper terminology, would you describe it as follows:
The Organizations field of variable c is set to the address of c.common casted to a pointer receiver value of OrganizationsService.
Or am I missing a bit of nuance when describing this?
Below are some relevant bits of source code that show where the variables are being defined.
// A Client manages communication with the GitHub API.
type Client struct {
clientMu sync.Mutex // clientMu protects the client during calls that modify the CheckRedirect func.
client *http.Client // HTTP client used to communicate with the API.
// Base URL for API requests. Defaults to the public GitHub API, but can be
// set to a domain endpoint to use with GitHub Enterprise. BaseURL should
// always be specified with a trailing slash.
BaseURL *url.URL
// Base URL for uploading files.
UploadURL *url.URL
// User agent used when communicating with the GitHub API.
UserAgent string
rateMu sync.Mutex
rateLimits [categories]Rate // Rate limits for the client as determined by the most recent API calls.
common service // Reuse a single struct instead of allocating one for each service on the heap.
// Services used for talking to different parts of the GitHub API.
Actions *ActionsService
Activity *ActivityService
Admin *AdminService
Apps *AppsService
Authorizations *AuthorizationsService
Checks *ChecksService
CodeScanning *CodeScanningService
Enterprise *EnterpriseService
Gists *GistsService
Git *GitService
Gitignores *GitignoresService
Interactions *InteractionsService
IssueImport *IssueImportService
Issues *IssuesService
Licenses *LicensesService
Marketplace *MarketplaceService
Migrations *MigrationService
Organizations *OrganizationsService
Projects *ProjectsService
PullRequests *PullRequestsService
Reactions *ReactionsService
Repositories *RepositoriesService
Search *SearchService
Teams *TeamsService
Users *UsersService
}
type service struct {
client *Client
}
The Organizations field of variable c is set to the address of c.common casted to a pointer receiver value of OrganizationsService.
This is close, but I'd make a few changes.
First:
casted
The operation being done here, according to official Go terminology, is a "conversion" not a cast.
Second:
c is set to the address of c.common...
While & is the "addressing" operator, the value that it results in is a pointer value. The literal contents of that value is the address. It's not incorrect to refer to addresses, but while analyzing syntax and structure, we would probably rather refer to values at the high level, rather than the contents of them.
Third:
pointer receiver value of...
The word "receiver" in Go refers to the receiver value in method call or method declaration, e.g.
func (v *Value) Method() {
}
Here v is the receiver, and it's type does happen to be a pointer type, but it doesn't have to be.
func (v Value) Method() {
}
This is also valid (though may have different and unintended effects compared to the first version). Regardless, the value in your question is not a receiver in any sense.
With the adjustments:
The Organizations field of variable c is set to the pointer to c.common converted to a pointer value to the OrganizationsService type.
Even still, we could restructure the sentence a bit so it is more similar to the order of operations as they happen in the program. It may seem natural to analyze things from left to right, but this is rarely the most comprehensible expression of code.
Here's an explanation that is, in my opinion, more natural.
The pointer to c's "common" field is converted to a pointer to OrganizationsService, and then assigned to c's "Organizations" field.

How to serialize a SecTrustRef object?

I have a SecTrustRef object from the system that I'd like to evaluate myself. Just calling SecTrustEvaluateAsync will be sufficient for this job. The problem is, I must evaluate it in a different process as only this other process has access to the keychains where the CA certificates are stored that may cause evaluation to succeed.
The two processes have an IPC link that allows me to exchange arbitrary byte data between them but I don't see any way to easily serialize a SecTrustRef into byte data and deserialize that data back to an object at the other process. There doesn't seem to be a persistent storage mechanism for SecTrustRef.
So am I overlooking something important here, or do I really have to get all the certs (SecTrustGetCertificateAtIndex) and all the policies (SecTrustCopyPolicies) and serialize these myself?
And if so, how would I serialize a policy?
For the certificate (SecCertificateRef) it's rather easy, I just call SecCertificateCopyData and later on SecCertificateCreateWithData.
But for policies I can only call SecPolicyCopyProperties on one side and later on SecPolicyCreateWithProperties, however the later one requires a 2nd parameter, a policyIdentifier and I see no way to get that value from an existing policy. What am I missing?
Reading through the source of the Security framework, I finally figured it out how to copy a SecPolicyRef:
SecPolicyCreateWithProperties wants what it calls a "policyIdentifier". It's a constant like kSecPolicyAppleIPsec.
This does not get stored directly by the function, it's comparing the value and calling dedicated internal "initializers" (like SecPolicyCreateIPsec).
These in turn call SecPolicyCreate (which is private). They end up passing the same identifier value that you passed to SecPolicyCreateWithProperties.
And this value then gets stored as-is in the _oid field!
The identifier is actually the OID. You can get it either via SecPolicyCopyProperties(policy) (stored in the dictionary with key kSecPolicyOid) or via SecPolicyGetOID (but that returns it as an inconvenient CSSM_OID). Some of those specialized initializers also use values from the properties dictionary passed to SecPolicyCreateWithProperties, those should be present in the copied properties dictionary already.
So this gives us:
Serialization:
CFDictionaryRef createSerializedPolicy(SecPolicyRef policy) {
// Already contains all the information needed.
return SecPolicyCopyProperties(policy);
}
Deserialization:
SecPolicyRef createDeserializedPolicy (CFDictionaryRef serialized) {
CFStringRef oid = CFDictionaryGetValue(serialized, kSecPolicyOid);
if (oid == NULL || CFGetTypeID(oid) != CFStringGetTypeID()) {
return NULL;
}
return SecPolicyCreateWithProperties(oid, serialized);
}
To reproduce the original SecTrustRef as closely as possible, the anchors need to be copied as well. There is an internal variable _anchorsOnly which is set to true once you set anchors. Unfortunately, there is no way to query this value and I've seen it being false in trusts passed by NSURLSession, for example. No idea yet on how to get this value in a public way.
Another problematic bit are the exceptions: if _exceptions is NULL but you query them via SecTrustCopyExceptions(trust), you do get data! And if you assign that to the deserialized trust via SecTrustSetExceptions(trust, exceptions) you suddenly end up with exceptions that were not there before and can change the evaluation result! (I've seen those suddenly appearing exceptions lead to an evaluation result of "proceed" instead of "recoverable trust failure").

Mapping of access mask in DACL for CNG keys

(Note: IMO the question is mainly about WinAPI and DACL and not about CNG, so please read on!)
I'm currently trying to modify the sample CNG key storage provider of Microsoft's Cryptographic Provider Development Kit in such a way that it does not store the keys in single files. However, I'm in trouble with the security descriptors that can be assigned to the private keys.
In the Certificates Snap-in of the Windows Server Management Console, private keys of certificates can be managed, i.e. the owner, DACL and SACL of a key can be changed, which results in a NCryptSetProperty call with a security descriptor as parameter. For the DACL, the snap-in only allows to allow/deny "full control" or "read", which results in the GENERIC_ALL or GENERIC_READ bit to be set in the access mask of the ACE.
As I have learnt, these generic bits need to be mapped to application specific rights - otherwise AccessCheck will not work. But do I really need to do this by hand???
CreatePrivateObjectSecurity+SetPrivateObjectSecurity does not always work since CreatePrivateObjectSecurity is very picky about the owner and group in the input security descriptor. Moreover, when the mapping is applied, the generic bits are cleared in the access mask, which results in the snap-in showing wrong settings (as I said, the snap-in only considers the GA and GR bits when displaying current permissions).
Seems I'm missing some pieces here...
in your CPSetProvParam implementation for PP_KEYSET_SEC_DESCR you got address of a SECURITY_DESCRIPTOR, which you need somehow apply to your private key storage. if your storage based on file(s) or registry key(s) ( in principle any kernel object type, but what more can be used here ?) you need call SetKernelObjectSecurity with file or key HANDLE (which must have WRITE_DAC access) (may be multiple time if you say have multiple files for store single key). in kernel GENERIC access to object will be auto converted to object specific rights.
if your implementation of storage not direct based on some kernel object, but custom - you need yourself at this point convert GENERIC access (0xF0000000 mask) to specific access rights (0x0000FFFF mask)
__________________ EDIT ____________________
after more check i found that provider must not only convert generic to specific access in CPSetProvParam but also convert specific to generic in CPGetProvParam despite this not directly point in documentation.
this is how MS_ENHANCED_PROV (implemented in rsaenh.dll) approximately do this:
void CheckAndChangeAccessMask(PSECURITY_DESCRIPTOR SecurityDescriptor)
{
BOOL bDaclPresent, bDaclDefaulted;
PACL Dacl;
ACL_SIZE_INFORMATION asi;
if (
GetSecurityDescriptorDacl(SecurityDescriptor, &bDaclPresent, &Dacl, &bDaclDefaulted)
&&
bDaclPresent
&&
Dacl
&&
GetAclInformation(Dacl, &asi, sizeof(asi), AclSizeInformation)
&&
asi.AceCount
)
{
union{
PVOID pAce;
PACE_HEADER pah;
PACCESS_ALLOWED_ACE paa;
};
do
{
if (GetAce(Dacl, --asi.AceCount, &pAce))
{
switch (pah->AceType)
{
case ACCESS_ALLOWED_ACE_TYPE:
case ACCESS_DENIED_ACE_TYPE:
ACCESS_MASK Mask = paa->Mask, Gen_Mask = 0;
if (Mask & FILE_READ_DATA)
{
Gen_Mask |= GENERIC_READ;
}
if (Mask & FILE_WRITE_DATA)
{
Gen_Mask |= GENERIC_ALL;
}
paa->Mask = Gen_Mask;
break;
}
}
} while (asi.AceCount);
}
}
so FILE_READ_DATA converted to GENERIC_READ and FILE_WRITE_DATA to GENERIC_ALL (this is exactly algorithm) - however you can look yourself code of rsaenh.CheckAndChangeAccessMask (name from pdb symbols)
rsaenh first get SD from file by GetNamedSecurityInfoW (SE_FILE_OBJECT) and then convert it specific to generic access.
here the call graph and modified DACL (in the top-right, modified ACCESS_MASK in red color)

How do I find out my multicast (Bonjour) hostname?

I've registered a record using the Bonjour API. Now I want to know the contents of the record I just published. I created it by specifying a NULL hostname, meaning, "use the daemon's default", but I can't find a simple way to query what that is!
With avahi, it's easy: I call avahi_client_get_host_name() to get the starting value of the machine's hostname.
For both avahi and Bonjour, the value of the SRV record can change during the lifetime of the registration - if the registration was done with a NULL hostname, the record's hostname is updated automatically when necessary. All I want here is a way to get the initial value of the hostname, at the time when I perform the registration.
Note that on my Snow Leopard test machine, the default multicast hostname is not the same as the machine's name from gethostname(2).
Four solutions I can think of:
Grab hostname in my process. It may be in there somewhere. I did a strings(3) search on a memory dump of my process, and found four instances of the multicast hostname in my address space, but that could be coincidence given the name is used for other things. Even if the string I'm after is in my process somewhere, I can't find an API to retrieve it sanely.
Query the hostname from the daemon. There may be some query I can send over the mach port to the daemon that fetches it? I can't find an API again. The relevant chunk of code is in the uDNS.c file in mDNSResponder, and doesn't seem to be exposed via the RPC interface.
I could just lookup the service I registered. This may involve a bit of network traffic though, so unless there's some guarantee that won't happen, I'm loathe to do it.
Re-implement the logic in uDNS.c. It grabs the machine's hostname from a combination of:
Dynamic DNS configuration
Statically configured multicast hostname
Reverse lookup of the primary interface's IPv4 address
It specifically doesn't use gethostname(2) or equivalent
Re-implementing that logic seems infeasible.
At the moment, I'm tending towards doing a lookup to grab the value of the initial SRV registration, but it doesn't seem ideal. What's the correct solution?
I needed to do exactly this. You want to use the ConvertDomainNameToCString macro (included in mDNSEmbeddedAPI.h), and you need access to the core mDNS structure.
Here's how you get the exact Bonjour/Zeroconf hostname that was registered:
char szHostname[512];
extern mDNS m;
ConvertDomainNameToCString(&m.MulticastHostname, szHostname);
I hope this helps you.
For the record, I went with (4), grabbing the machine's configuration to pull together the hostname the daemon is using without having to query it.
static char* getBonjourDefaultHost()
{
char* rv = 0;
#ifdef __APPLE__
CFStringRef name = SCDynamicStoreCopyLocalHostName(NULL);
if (name) {
int len = CFStringGetLength(name);
rv = new char[len*4+1];
CFStringGetCString(name, rv, len*4+1, kCFStringEncodingUTF8);
CFRelease(name);
}
// This fallback is completely incorrect, but why should we care...
// Mac does something crazy like <sysctl hw.model>-<MAC address>.
if (!rv)
rv = GetHostname(); // using gethostname(2)
#elif defined(WIN32)
CHAR tmp[256+1];
ULONG namelength = sizeof(tmp);
DynamicFn<BOOL (WINAPI*)(COMPUTER_NAME_FORMAT,LPSTR,LPDWORD)>
GetComputerNameExA_("Kernel32", "GetComputerNameExA");
if (!GetComputerNameExA_.isValid() ||
!(*GetComputerNameExA_)(ComputerNamePhysicalDnsHostname, tmp, &namelength))
tmp[0] = 0;
// Roughly correct; there's some obscure string cleaning mDNSResponder does
// to tidy up funny international strings.
rv = tmp[0] ? strdup(tmp) : strdup("My Computer");
#elif defined(__sun)
// This is exactly correct! What a relief.
rv = GetHostName();
#else
#error Must add platform mDNS daemon scheme
#endif
return rv;
}
From the command line one can obtain the local (Bonjour) hostname using the scutil command:
scutil --get LocalHostName
Programmatically this is obtainable using the kSCPropNetLocalHostName key via the SystemConfiguration Framework.
I know this is already answered, but I went looking to see how to do it with the SystemConfiguration framework based on what Pierz said in the second half of his answer.
This is what I got working, figured it might save someone else googling this some time:
In Swift:
import SystemConfiguration
let store = SCDynamicStoreCreate(nil, "ipmenu" as CFString, nil, nil )
if let hostNames = SCDynamicStoreCopyValue(store, "Setup:/Network/HostNames" as CFString){
if let hostName:String = (hostNames[kSCPropNetLocalHostName]) as? String {
print("Host name:\(hostName)")
}
}

The specified format name does not support the requested operation. For example, a direct queue format name cannot be deleted

I'm trying to send a message to a queue using a Message object and am getting the error
The specified format name does not support the requested operation. For example, a direct queue format name cannot be deleted.
Here is the code.
Order ord = new Order(new Guid(), "Smith & Smith");
Message orderMessage = new Message(ord);
orderMessage.UseEncryption = true;
orderMessage.EncryptionAlgorithm = EncryptionAlgorithm.Rc2;
orderMessage.Recoverable = true;
orderMessage.Priority = MessagePriority.VeryHigh;
orderMessage.TimeToBeReceived = TimeSpan.FromHours(1);
orderMessage.UseJournalQueue = true;
orderMessage.Body = "Test Encryption";
queue.Send(orderMessage, "Encrypted Order");
Any help with this is appreciated.
Tom
Did you ever solve this? I came across this problem myself and found out I needed to use (just like the error says) a different format name.
The strange thing was that if I set UseAuthentication property using the MQ certificate, then it worked. But if I also wanted to set UseEncryption, then it did not work.
You do not specify your queue/server setup/formats, but I suspect you're trying to send from one machine to another machine's public queue within the same domain, using DIRECT formatname? As the MQ Manager will use the domain AD to lookup the certificate and queue details, it raises an exception as the format name is invalid (not the same as specified in the AD). So instead of using the direct format, use the queue ID to define the formatname. I switched this:
"FormatName:Direct=TCP:111.222.1.22\your_public_queue"
with this:
"FormatName:PUBLIC=7EB2A53C-7593-462C-A568-5A0EFA26D91D"
Now it worked. You can find your queue ID by right-clicking your queue on the receiver machine and then go to Properties->General and see the value specified in field "ID".
I have found that getting the FormatName correct whether public or private in nature will save hours of work. It's incredibly important to understand the setup of each (Public requiring AD and private does not when access remotely). This is a great summary of FormatName.
https://blogs.msdn.microsoft.com/johnbreakwell/2009/02/26/difference-between-path-name-and-format-name-when-accessing-msmq-queues/
One note on this issue, if your queue format name starts this way: "FormatName:Direct=" then you will receive the error "The specified format name does not support the requested operation. For example, a direct queue format name cannot be deleted" if you try to access the queue's QueueName property. Use the queue's FormatName property instead.

Resources