////////////////////////////////////////////////////////////////////
//
// skravel - NT SAM enumeration by looking up RIDs
//
// (c)2000 Olle Segerdahl <olle@nxs.se>
//
// This program is BeerWare, if you like it, buy me a beer!
// If you don't like it, don't use it. Give credit where due.
//
////////////////////////////////////////////////////////////////////

#include <windows.h>
#include <stdio.h>

#include <lmaccess.h>	// NetUserGetInfo()

// ugly globals
int verbose = 0;



//////////////////////////////////////////////////////////////////////////////
// GetTextualSid	-	snarfed MSDN code to convert SID 2 ascii values
//					I'm too lazy right now to build my own... so sue me...
//////////////////////////////////////////////////////////////////////////////
BOOL GetTextualSid(PSID pSid, LPSTR szTextualSid, 
     LPDWORD dwBufferLen) {

      PSID_IDENTIFIER_AUTHORITY psia;
      DWORD dwSubAuthorities;
      DWORD dwSidRev = SID_REVISION;
      DWORD dwCounter;
      DWORD dwSidSize;

      // Test if SID passed in is valid.
      if(!IsValidSid(pSid)) 
         return FALSE;

      // Obtain SidIdentifierAuthority.
      psia = GetSidIdentifierAuthority(pSid);

      // Obtain sidsubauthority count.
      dwSubAuthorities = *GetSidSubAuthorityCount(pSid);

      // Compute buffer length.
      // S-SID_REVISION- + identifierauthority- + subauthorities- + NULL
      dwSidSize = (15 + 12 + (12 * dwSubAuthorities) + 1) * sizeof(TCHAR);

      // Check provided buffer length.
      // If not large enough, indicate proper size and setlasterror
      if (*dwBufferLen < dwSidSize) {

        *dwBufferLen = dwSidSize;
        SetLastError(ERROR_INSUFFICIENT_BUFFER);
        return FALSE;
      }

      // Prepare S-SID_REVISION-.
      dwSidSize = wsprintf(szTextualSid, TEXT("S-%lu-"), dwSidRev);

      // Prepare SidIdentifierAuthority.
      if ((psia->Value[0] != 0) || (psia->Value[1] != 0)) {

         dwSidSize += wsprintf(szTextualSid + lstrlen(szTextualSid),
               TEXT("0x%02hx%02hx%02hx%02hx%02hx%02hx"),
               (USHORT) psia->Value[0],
               (USHORT) psia->Value[1],
               (USHORT) psia->Value[2],
               (USHORT) psia->Value[3],
               (USHORT) psia->Value[4],
               (USHORT) psia->Value[5]);
      
      } else {
      
         dwSidSize += wsprintf(szTextualSid + lstrlen(szTextualSid),
               TEXT("%lu"),
               (ULONG) (psia->Value[5]      ) +
               (ULONG) (psia->Value[4] <<  8) +
               (ULONG) (psia->Value[3] << 16) +
               (ULONG) (psia->Value[2] << 24));
      }

      // Loop through SidSubAuthorities.
      for (dwCounter = 0; dwCounter < dwSubAuthorities; dwCounter++) {

         dwSidSize += wsprintf(szTextualSid + dwSidSize, TEXT("-%lu"),
               *GetSidSubAuthority(pSid, dwCounter));
      }

      return TRUE;
} 



//////////////////////////////////////////////////////////////////////////////
// sid2user	-	looks up name of Sid on machine and prints it
//////////////////////////////////////////////////////////////////////////////
sid2user(char *sid, char *machine)
{
	SID *psid;
	int sidl = 127;
	int domainl = 127;
	int namel = 127;
	char domain[128], name[128], *type;
	SID_NAME_USE use;

	int err;

	psid = (SID *)sid;

	err = LookupAccountSidA(
		machine,  // name of local or remote computer
		sid,      // security identifier
		name,     // account name buffer
		&namel,   // size of account name buffer
		domain,   // domain name
		&domainl, // size of domain name buffer
		&use      // SID type
	);
		
	
	if (err != 1) {

		err = GetLastError();

		if (err != ERROR_NONE_MAPPED) {
			fprintf(stderr,"\nRID: %d ",psid->SubAuthority[psid->SubAuthorityCount-1]);
			fprintf(stderr,"LookupAaccountSid Error: %d\n",err);
		}


	} else { /* err != 1 */

		if (verbose) {

			fprintf(stdout, "RID %d: \"%s\" is of type ",psid->SubAuthority[psid->SubAuthorityCount-1], name);
			
			switch (use) {
			case SidTypeUser:
				type = "User";
				break;
			case SidTypeGroup:
				type = "Group";
				break;
			case SidTypeDomain:
				type = "Domain";
				break;
			case SidTypeAlias:
				type = "Alias";
				break;
			case SidTypeWellKnownGroup:
				type = "WellKnownGroup";
				break;
			case SidTypeDeletedAccount:
				type = "DeletedAccount";
				break;
			case SidTypeInvalid:
				type = "InvalidType";
				break;
			case SidTypeComputer:
				type = "Computer";
				break;
			case SidTypeUnknown:
			default:
				type = "UnknownType";
				break;
			}

			fprintf(stdout, "%s\n", type);

		} else { /* verbose */

			if (use == SidTypeUser)
				fprintf(stdout,"%s\n",name);

		}
	}

}


void usage(char **argv)
{
		fprintf(stderr,"\n skravel v0.96     (c)2000 olle@nxs.se\n\n");
		fprintf(stderr," enumerate NT SAM accounts by RID\n\n");
		fprintf(stderr," usage:	%s <host> <domain> [from-to] [-v]\n\n",argv[0]);
		fprintf(stderr," host = system to query, requires IPC$ connection\n");
		fprintf(stderr," domain = NT domain to enumerate accounts of\n");
		fprintf(stderr," from = min RID value to enumerate (default 0)\n");
		fprintf(stderr," to = max RID value to enumerate (default 1500)\n");
		fprintf(stderr," -v = enumerate RIDs of all types, not just users\n\n");
		exit(0);
}

//////////////////////////////////////////////////////////////////////////////
// main	-	entrypoint of champions
//////////////////////////////////////////////////////////////////////////////
main(int argc, char **argv)
{

	SID *psid;
	char sid[128];
	int sidl = 127;
	char domain[128];
	int domainl = 127;
	char buf[128];
	SID_NAME_USE use;
	unsigned int minrid = 0;
	unsigned int maxrid = 1500;
	char *cp;
	int err;

	if (argc < 3) {
		usage(argv);
	}

	// bad argument parsing
	if (argc == 4) {
		if (*(argv[3]+1)=='v') {
			verbose = 1;
		} else {
			cp = strchr(argv[3], '-');
			if (cp) {
				*cp = '\0';
				minrid = atoi(argv[3]);
				maxrid = atoi(cp+1);
			} else {
				usage(argv);
			}
		}
	}

	if (argc == 5) {
		if (*(argv[3]+1)=='v') {
			verbose = 1;
		} else {
			cp = strchr(argv[3], '-');
			if (cp) {
				*cp = '\0';
				minrid = atoi(argv[3]);
				maxrid = atoi(cp+1);
			} else {
				usage(argv);
			}
		}
		if (*(argv[4]+1)=='v') {
			verbose = 1;
		} else {
			cp = strchr(argv[4], '-');
			if (cp) {
				*cp = '\0';
				minrid = atoi(argv[4]);
				maxrid = atoi(cp+1);
			} else {
				usage(argv);
			}
		}
	}

	fprintf(stderr,"\nLooking up \"%s\" on system \"%s\"\n",argv[2],argv[1]);

	err = LookupAccountNameA(
		argv[1],  // system name
		argv[2],  // account name
		&sid,     // security identifier
		&sidl,    // size of security identifier
		domain,   // domain name
		&domainl, // size of domain name
		&use      // SID-type indicator
	);
	
	if (err != 1) {

		err = GetLastError();
		
		if (err == ERROR_NONE_MAPPED)

			fprintf(stderr,"\n%s: No such name at \"%s\"\n",argv[2], argv[1]); 

		else if (err == RPC_S_SERVER_UNAVAILABLE)

			fprintf(stderr,"\nCould not contact system \"%s\"\n", argv[1]); 

		else

			fprintf(stderr,"\nError: %d\n",err);

		exit(1);


	}


	psid = (SID *)sid;
	if (use == SidTypeDomain) {

		fprintf(stderr,"\n\"%s\" is a domain, enumerating RID's %i to %i.\n",argv[2],minrid, maxrid);

		GetTextualSid(&sid,buf,&sidl);
		fprintf(stderr,"\nDomain: %s\n",domain);	
		fprintf(stderr,"SID: %s\n\n",buf);

		if (psid->SubAuthority[psid->SubAuthorityCount-1] == 0xFFFFFFFF)	// workaround for NT4 ??
			--psid->SubAuthorityCount;

		++psid->SubAuthorityCount;

		// loop RIDs
		for (psid->SubAuthority[psid->SubAuthorityCount-1] = minrid ;
				psid->SubAuthority[psid->SubAuthorityCount-1] <= maxrid ; 
				++psid->SubAuthority[psid->SubAuthorityCount-1]) {

			sid2user(sid,argv[1]);
		}

	} else {

		verbose = 1;

		GetTextualSid(&sid,buf,&sidl);
		fprintf(stderr,"\nDomain: %s\n",domain);	
		fprintf(stderr,"SID: %s\n\n",buf);
		sid2user(sid,argv[1]);

	}

}
