You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1365 lines
44 KiB
1365 lines
44 KiB
/*
|
|
usage:
|
|
|
|
winhttpcertcfg [-?] : to view help information
|
|
|
|
winhttpcertcfg [-i PFXFile | -g | -r | -l]
|
|
-c CertLocation\CertStore [-a Account] [-s SubjectStr] [-p PFXPassword]
|
|
|
|
-i PFXFile : Import cert via specified .pfx filename given
|
|
with the option. This option also requires
|
|
-a and -c to indicate destination (doesn't support -a
|
|
in conjunction with importing just yet).
|
|
|
|
-g : Grant access to private key for already
|
|
installed cert indicated by subject string.
|
|
This option also requires -a, -c, and -s.
|
|
|
|
-r : Remove access to private key for specified
|
|
account name and certificate. This option
|
|
also requires -a, -c, -s to specify the
|
|
account and certificate information.
|
|
|
|
-l : List accounts that have access to the private
|
|
key of enumerated certs specified via other
|
|
filter options. This option also requires
|
|
-c and -s to specify which certificate should
|
|
be queried.
|
|
|
|
Description of addtional options:
|
|
-c LOCAL_MACHINE|CURRENT_USER\CertStore (Example: LOCAL_MACHINE\MY)
|
|
|
|
-a Account (Represents user or domain account on the machine.
|
|
Examples: IWAM_TESTMACHINE, TESTUSER, or TESTDOMAIN\DOMAINUSER)
|
|
|
|
-s SubjectStr (Case-insensitive search string for finding the first enumerated
|
|
certificate with a subject name that contains this substring.)
|
|
|
|
-p PFXPassword (Password to use when importing a PFX file. Only used with -i.)
|
|
*/
|
|
|
|
#include <windows.h>
|
|
#include <cryptui.h>
|
|
#include <wincrypt.h>
|
|
#include <stdio.h>
|
|
|
|
typedef DWORD (WINAPI* CRYPTUIWIZIMPORT) (DWORD, HWND, LPCWSTR, PCCRYPTUI_WIZ_IMPORT_SRC_INFO, HCERTSTORE);
|
|
typedef BOOL (WINAPI* SETSECURITYDESCRIPTORCONTROL) (PSECURITY_DESCRIPTOR,
|
|
SECURITY_DESCRIPTOR_CONTROL,
|
|
SECURITY_DESCRIPTOR_CONTROL);
|
|
|
|
static const char gc_szLocalMachine[] = "LOCAL_MACHINE";
|
|
static const char gc_szCurrentUser[] = "CURRENT_USER";
|
|
|
|
enum ARGTYPE
|
|
{
|
|
ARGS_HELP,
|
|
ARGS_IMPORT_PFX,
|
|
ARGS_ADD_PRIVATE_KEY_ACCESS,
|
|
ARGS_REMOVE_PRIVATE_KEY_ACCESS,
|
|
ARGS_LIST_PRIVATE_KEY_ACCESS
|
|
};
|
|
|
|
|
|
struct ARGS
|
|
{
|
|
ARGTYPE Command;
|
|
|
|
LPWSTR pwszPFXFile;
|
|
LPWSTR pwszCertStore;
|
|
LPWSTR pwszCertSubject;
|
|
LPWSTR pwszPFXPassword;
|
|
LPSTR pszDomain;
|
|
LPSTR pszAccount;
|
|
BOOL fUseLocalMachine;
|
|
};
|
|
|
|
|
|
void ParseArguments(int argc, char **argv, ARGS *pArgs);
|
|
DWORD AsciiToWideChar(const char * pszA, LPWSTR * ppszW);
|
|
DWORD ImportPFXFile(ARGS *pArgs);
|
|
VOID DumpSid(BYTE *pSid);
|
|
VOID DumpCertInfo(PCCERT_CONTEXT pCertContext);
|
|
DWORD GetAccountForSid(BYTE *pSid, LPTSTR *ppszDomain, LPTSTR *ppszAccount);
|
|
DWORD GetSidForAccount(LPCTSTR pszDomain, LPCTSTR pszAccount, BYTE **ppSid, char **ppszDomain);
|
|
DWORD DumpAccessAllowedList(PACL pDacl);
|
|
DWORD DoPrivateKeyAccessAction(ARGS *pArgs);
|
|
DWORD ProcessCertContext(PCCERT_CONTEXT pCertContext, BYTE *pSid, ARGTYPE eCommand);
|
|
BOOL CheckForRootCert(PCCERT_CONTEXT pCertContext);
|
|
DWORD AddPrivateKeyAccess(PACL pDacl, BYTE *pSid, PACL *ppNewDacl);
|
|
DWORD RemovePrivateKeyAccess(PACL pDacl, BYTE *pSid);
|
|
BOOL MySetSecurityDescriptorControl(PSECURITY_DESCRIPTOR pSD,
|
|
SECURITY_DESCRIPTOR_CONTROL ControlBitsOfInterest,
|
|
SECURITY_DESCRIPTOR_CONTROL ControlBitsToSet);
|
|
|
|
void
|
|
ParseArguments(int argc, char **argv, ARGS *pArgs)
|
|
{
|
|
char *pszBreak;
|
|
|
|
ZeroMemory((PVOID) pArgs, sizeof(ARGS));
|
|
|
|
pArgs->Command = ARGS_HELP;
|
|
pArgs->fUseLocalMachine = TRUE;
|
|
|
|
for (; argc > 0; argc--, argv++)
|
|
{
|
|
if (((argv[0][0] != '-') && (argv[0][0] != '/')) || (lstrlen(argv[0]) != 2))
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
switch (tolower(argv[0][1]))
|
|
{
|
|
case 'l':
|
|
pArgs->Command = ARGS_LIST_PRIVATE_KEY_ACCESS;
|
|
break;
|
|
|
|
case 'g':
|
|
pArgs->Command = ARGS_ADD_PRIVATE_KEY_ACCESS;
|
|
break;
|
|
|
|
case 'i':
|
|
{
|
|
TCHAR szShortPath[MAX_PATH];
|
|
BOOL fUseConvertedPath = TRUE;
|
|
|
|
argc--;
|
|
argv++;
|
|
|
|
if (argc == 0)
|
|
{
|
|
// error: no PFX file specified
|
|
goto ErrorExit;
|
|
}
|
|
|
|
// PFX Import API doesn't handle long filenames
|
|
if (GetShortPathName(argv[0], szShortPath, MAX_PATH) > MAX_PATH)
|
|
{
|
|
fUseConvertedPath = FALSE;
|
|
}
|
|
|
|
if (ERROR_SUCCESS == AsciiToWideChar(fUseConvertedPath ? szShortPath : argv[0],
|
|
&pArgs->pwszPFXFile))
|
|
{
|
|
pArgs->Command = ARGS_IMPORT_PFX;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
case 'r':
|
|
pArgs->Command = ARGS_REMOVE_PRIVATE_KEY_ACCESS;
|
|
break;
|
|
|
|
case 'c':
|
|
argc--;
|
|
argv++;
|
|
|
|
if (argc > 0 &&
|
|
(pszBreak = strchr(argv[0], '\\')) != NULL)
|
|
{
|
|
if (_strnicmp(argv[0], gc_szLocalMachine, strlen(gc_szLocalMachine)) == 0)
|
|
{
|
|
pArgs->fUseLocalMachine = TRUE;
|
|
}
|
|
else if (_strnicmp(argv[0], gc_szCurrentUser, strlen(gc_szCurrentUser)) == 0)
|
|
{
|
|
pArgs->fUseLocalMachine = FALSE;
|
|
}
|
|
else
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
// increment to skip the backslash
|
|
pszBreak++;
|
|
if (ERROR_SUCCESS == AsciiToWideChar(pszBreak, &pArgs->pwszCertStore))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
goto ErrorExit;
|
|
|
|
case 's':
|
|
argc--;
|
|
argv++;
|
|
|
|
if (argc <= 0 ||
|
|
ERROR_SUCCESS != AsciiToWideChar(argv[0], &pArgs->pwszCertSubject))
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
break;
|
|
|
|
case 'a':
|
|
argc--;
|
|
argv++;
|
|
|
|
if (argc > 0)
|
|
{
|
|
pArgs->pszAccount = strchr(argv[0], '\\');
|
|
if (pArgs->pszAccount)
|
|
{
|
|
// Break the two apart by removing the slash
|
|
*(pArgs->pszAccount) = '\0';
|
|
pArgs->pszAccount++;
|
|
pArgs->pszDomain = argv[0];
|
|
}
|
|
else
|
|
{
|
|
pArgs->pszAccount = argv[0];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
break;
|
|
|
|
case 'p':
|
|
argc--;
|
|
argv++;
|
|
|
|
if (argc <= 0 ||
|
|
ERROR_SUCCESS != AsciiToWideChar(argv[0], &pArgs->pwszPFXPassword))
|
|
{
|
|
// error: no password specified
|
|
goto ErrorExit;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
goto ErrorExit;
|
|
|
|
}
|
|
}
|
|
|
|
// Now verify that we've obtained all the needed
|
|
// options based on the desired config operation.
|
|
if (pArgs->Command != ARGS_HELP)
|
|
{
|
|
// All options need -c.
|
|
// All but -i need -s, and all but -i prohibit -p.
|
|
// All but -l need -a.
|
|
// -a not allowed for -l.
|
|
// -s not allowed for -i.
|
|
if (pArgs->pwszCertStore == NULL ||
|
|
(pArgs->Command != ARGS_IMPORT_PFX &&
|
|
(pArgs->pwszCertSubject == NULL || pArgs->pwszPFXPassword)) ||
|
|
(pArgs->Command == ARGS_LIST_PRIVATE_KEY_ACCESS && pArgs->pszAccount) ||
|
|
(pArgs->Command == ARGS_IMPORT_PFX && pArgs->pwszCertSubject))
|
|
{
|
|
// Incorrect set of options were passed, so flip this to
|
|
// displaying the usage.
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
|
|
return;
|
|
|
|
ErrorExit:
|
|
pArgs->Command = ARGS_HELP;
|
|
return;
|
|
}
|
|
|
|
|
|
//
|
|
// AsciiToWideChar was borrowed from WinHttp\v5\common\util.cxx
|
|
//
|
|
|
|
DWORD
|
|
AsciiToWideChar(const char * pszA, LPWSTR * ppszW)
|
|
{
|
|
DWORD cchA;
|
|
DWORD cchW;
|
|
|
|
*ppszW = NULL;
|
|
|
|
if (!pszA)
|
|
return ERROR_SUCCESS;
|
|
|
|
cchA = lstrlenA(pszA);
|
|
|
|
// Determine how big the widechar string will be
|
|
cchW = MultiByteToWideChar(CP_ACP, 0, pszA, cchA, NULL, 0);
|
|
|
|
*ppszW = new WCHAR[cchW+1];
|
|
|
|
if (!*ppszW)
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
cchW = MultiByteToWideChar(CP_ACP, 0, pszA, cchA, *ppszW, cchW);
|
|
|
|
(*ppszW)[cchW] = 0;
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
DWORD
|
|
ImportPFXFile(ARGS *pArgs)
|
|
{
|
|
CRYPTUI_WIZ_IMPORT_SRC_INFO importSrc;
|
|
HCERTSTORE hDestCertStore = NULL;
|
|
HCERTSTORE hTempCertStore = NULL;
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
HINSTANCE hCryptUILib = NULL;
|
|
CRYPTUIWIZIMPORT pfnCryptUIWizImport;
|
|
|
|
hCryptUILib = LoadLibrary(TEXT("cryptui.dll"));
|
|
if (hCryptUILib == NULL)
|
|
{
|
|
dwError = GetLastError();
|
|
fprintf(stderr,
|
|
"Error: Failed to load cryptography component with error code 0x%X\n",
|
|
dwError);
|
|
goto Cleanup;
|
|
}
|
|
|
|
pfnCryptUIWizImport = (CRYPTUIWIZIMPORT)GetProcAddress(hCryptUILib, "CryptUIWizImport");
|
|
if (pfnCryptUIWizImport == NULL)
|
|
{
|
|
dwError = GetLastError();
|
|
fprintf(stderr, "Error: Could not find needed DLL entry point\n");
|
|
goto Cleanup;
|
|
}
|
|
|
|
hDestCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM,
|
|
0,
|
|
NULL,
|
|
CERT_STORE_OPEN_EXISTING_FLAG |
|
|
(pArgs->fUseLocalMachine ? CERT_SYSTEM_STORE_LOCAL_MACHINE :
|
|
CERT_SYSTEM_STORE_CURRENT_USER),
|
|
pArgs->pwszCertStore);
|
|
|
|
hTempCertStore = CertOpenStore(CERT_STORE_PROV_MEMORY,
|
|
0,
|
|
NULL,
|
|
0,
|
|
NULL);
|
|
|
|
if (!hDestCertStore || !hTempCertStore)
|
|
{
|
|
dwError = GetLastError();
|
|
fprintf(stderr,
|
|
"Error: Failed to open certificate store with error code 0x%X\n",
|
|
dwError);
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Ensure that it's really a PFX file that can be imported.
|
|
if(CryptQueryObject(CERT_QUERY_OBJECT_FILE,
|
|
pArgs->pwszPFXFile,
|
|
CERT_QUERY_CONTENT_FLAG_PFX,
|
|
CERT_QUERY_FORMAT_FLAG_ALL,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL))
|
|
{
|
|
|
|
memset(&importSrc, 0, sizeof(CRYPTUI_WIZ_IMPORT_SRC_INFO));
|
|
importSrc.dwSize = sizeof(CRYPTUI_WIZ_IMPORT_SRC_INFO);
|
|
importSrc.dwSubjectChoice = CRYPTUI_WIZ_IMPORT_SUBJECT_FILE;
|
|
importSrc.pwszFileName = pArgs->pwszPFXFile;
|
|
importSrc.pwszPassword = pArgs->pwszPFXPassword;
|
|
|
|
if ((*pfnCryptUIWizImport)(CRYPTUI_WIZ_NO_UI |
|
|
(pArgs->fUseLocalMachine ? CRYPTUI_WIZ_IMPORT_TO_LOCALMACHINE :
|
|
CRYPTUI_WIZ_IMPORT_TO_CURRENTUSER) |
|
|
CRYPTUI_WIZ_IMPORT_ALLOW_CERT,
|
|
NULL,
|
|
NULL,
|
|
&importSrc,
|
|
hTempCertStore))
|
|
{
|
|
DWORD dwImportCount = 0;
|
|
DWORD dwAclCount = 0;
|
|
BYTE *pSid = NULL;
|
|
LPSTR pszDomain = NULL;
|
|
|
|
dwError = GetSidForAccount(pArgs->pszDomain,
|
|
pArgs->pszAccount,
|
|
&pSid,
|
|
&pszDomain);
|
|
if (ERROR_SUCCESS != dwError)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Extract cert context(s) from temporary store and copy to
|
|
// destination store. Need to enumerate and import one
|
|
// at a time, so the private key access can also be modified.
|
|
PCCERT_CONTEXT pCertContext = NULL;
|
|
PCCERT_CONTEXT pDestCertContext = NULL;
|
|
|
|
importSrc.dwSubjectChoice = CRYPTUI_WIZ_IMPORT_SUBJECT_CERT_CONTEXT;
|
|
|
|
while (ERROR_SUCCESS == dwError &&
|
|
(pCertContext = CertEnumCertificatesInStore(hTempCertStore, pCertContext)) != NULL)
|
|
{
|
|
// Root cert is last...break and add it to the root store.
|
|
if (CheckForRootCert(pCertContext))
|
|
break;
|
|
|
|
// Overwrite if already installed.
|
|
if (CertAddCertificateContextToStore(hDestCertStore,
|
|
pCertContext,
|
|
CERT_STORE_ADD_REPLACE_EXISTING,
|
|
&pDestCertContext))
|
|
{
|
|
// Let the user see info regarding the cert imported
|
|
fprintf(stdout, "Imported certificate:\n");
|
|
DumpCertInfo(pCertContext);
|
|
fprintf(stdout, "\n");
|
|
dwError = ProcessCertContext(pDestCertContext,
|
|
pSid,
|
|
ARGS_ADD_PRIVATE_KEY_ACCESS);
|
|
|
|
if (ERROR_SUCCESS == dwError)
|
|
{
|
|
dwAclCount++;
|
|
}
|
|
CertFreeCertificateContext(pDestCertContext);
|
|
|
|
dwImportCount++;
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr,
|
|
"Error: Failed to import a certificate, error = 0x%X\n\n",
|
|
GetLastError());
|
|
}
|
|
}
|
|
if (pCertContext)
|
|
{
|
|
CertCloseStore(hDestCertStore, 0);
|
|
|
|
hDestCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM,
|
|
0,
|
|
NULL,
|
|
CERT_STORE_OPEN_EXISTING_FLAG |
|
|
(pArgs->fUseLocalMachine ? CERT_SYSTEM_STORE_LOCAL_MACHINE :
|
|
CERT_SYSTEM_STORE_CURRENT_USER),
|
|
L"Root");
|
|
|
|
if (!hDestCertStore)
|
|
{
|
|
dwError = GetLastError();
|
|
fprintf(stderr, "Error: Unable to import root certificate\n\n");
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (!CertAddCertificateContextToStore(hDestCertStore,
|
|
pCertContext,
|
|
CERT_STORE_ADD_REPLACE_EXISTING,
|
|
NULL))
|
|
{
|
|
fprintf(stderr, "Warning: Failed to import root certificate\n\n");
|
|
}
|
|
|
|
CertFreeCertificateContext(pCertContext);
|
|
}
|
|
|
|
if (pSid)
|
|
LocalFree(pSid);
|
|
|
|
if (pszDomain)
|
|
LocalFree(pszDomain);
|
|
}
|
|
else
|
|
{
|
|
dwError = GetLastError();
|
|
fprintf(stderr,
|
|
"Error: Unable to import contents of PFX file.\n"
|
|
" Please make sure the filename and path,\n"
|
|
" as well as the password, are correct.\n\n",
|
|
dwError);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dwError = GetLastError();
|
|
if (dwError == ERROR_FILE_NOT_FOUND)
|
|
{
|
|
fprintf(stderr,"Error: PFX file was not found\n");
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr,"Error: Unable to open PFX file\n");
|
|
}
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
if (hTempCertStore)
|
|
CertCloseStore(hTempCertStore, CERT_CLOSE_STORE_FORCE_FLAG);
|
|
if (hDestCertStore)
|
|
CertCloseStore(hDestCertStore, CERT_CLOSE_STORE_FORCE_FLAG);
|
|
|
|
if (hCryptUILib)
|
|
{
|
|
FreeLibrary(hCryptUILib);
|
|
}
|
|
|
|
return dwError;
|
|
}
|
|
|
|
|
|
VOID
|
|
DumpSid(BYTE *pSid)
|
|
{
|
|
LPTSTR pszDomain = NULL;
|
|
LPTSTR pszAccount = NULL;
|
|
|
|
if (ERROR_SUCCESS == GetAccountForSid(pSid, &pszDomain, &pszAccount))
|
|
{
|
|
if (pszDomain)
|
|
{
|
|
fprintf(stdout,
|
|
" %s\\%s\n",
|
|
pszDomain,
|
|
pszAccount);
|
|
}
|
|
else
|
|
{
|
|
fprintf(stdout,
|
|
" %s\n",
|
|
pszDomain,
|
|
pszAccount);
|
|
}
|
|
if (pszDomain)
|
|
LocalFree(pszDomain);
|
|
if (pszAccount)
|
|
LocalFree(pszAccount);
|
|
}
|
|
}
|
|
|
|
|
|
DWORD
|
|
GetAccountForSid(BYTE *pSid,
|
|
LPTSTR *ppszDomain,
|
|
LPTSTR *ppszAccount)
|
|
{
|
|
DWORD cbAccountLength = 0;
|
|
DWORD cbDomainLength = 0;
|
|
SID_NAME_USE eUse;
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
|
|
if (!pSid || !ppszDomain || !ppszAccount || !IsValidSid(pSid))
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
*ppszDomain = NULL;
|
|
*ppszAccount = NULL;
|
|
|
|
if (!LookupAccountSid(NULL,
|
|
pSid,
|
|
*ppszAccount,
|
|
&cbAccountLength,
|
|
*ppszDomain,
|
|
&cbDomainLength,
|
|
&eUse))
|
|
{
|
|
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
return GetLastError();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (cbDomainLength)
|
|
{
|
|
*ppszDomain = (LPTSTR) LocalAlloc(LPTR, cbDomainLength * sizeof(TCHAR));
|
|
if (!*ppszDomain)
|
|
{
|
|
dwError = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto ErrorExit;
|
|
};
|
|
}
|
|
|
|
*ppszAccount = (LPTSTR) LocalAlloc(LPTR, cbAccountLength * sizeof(TCHAR));
|
|
if (!*ppszAccount)
|
|
{
|
|
dwError = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
if (!LookupAccountSid(NULL,
|
|
pSid,
|
|
*ppszAccount,
|
|
&cbAccountLength,
|
|
*ppszDomain,
|
|
&cbDomainLength,
|
|
&eUse))
|
|
{
|
|
dwError = GetLastError();
|
|
goto ErrorExit;
|
|
}
|
|
|
|
// Made it through, should be ERROR_SUCCESS
|
|
return dwError;
|
|
|
|
ErrorExit:
|
|
if (*ppszDomain)
|
|
{
|
|
LocalFree(*ppszDomain);
|
|
*ppszDomain = NULL;
|
|
}
|
|
if (*ppszAccount)
|
|
{
|
|
LocalFree(*ppszAccount);
|
|
*ppszAccount = NULL;
|
|
}
|
|
|
|
fprintf(stderr,"Error: Unable to determine account name for SID, error = 0x%X\n", dwError);
|
|
|
|
return dwError;
|
|
}
|
|
|
|
|
|
DWORD
|
|
GetSidForAccount(LPCTSTR pszDomain,
|
|
LPCTSTR pszAccount,
|
|
BYTE **ppSid,
|
|
char **ppszDomain)
|
|
{
|
|
SID_NAME_USE su;
|
|
DWORD cbSidLength = 0;
|
|
DWORD cbDomainLength = 0;
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
|
|
if (!pszAccount || !ppSid || !ppszDomain)
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
*ppSid = NULL;
|
|
*ppszDomain = NULL;
|
|
|
|
// Determine the SID that belongs to the user. Make first call to
|
|
// get the needed buffer sizes.
|
|
if (!(LookupAccountName(pszDomain,
|
|
pszAccount,
|
|
*ppSid,
|
|
&cbSidLength,
|
|
*ppszDomain,
|
|
&cbDomainLength,
|
|
&su)))
|
|
{
|
|
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
dwError = GetLastError();
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
*ppSid = (BYTE *) LocalAlloc(LPTR, cbSidLength);
|
|
if (!*ppSid)
|
|
{
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
};
|
|
|
|
*ppszDomain = (char *) LocalAlloc(LPTR, cbDomainLength);
|
|
if (!*ppszDomain)
|
|
{
|
|
dwError = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
if (!LookupAccountName(pszDomain,
|
|
pszAccount,
|
|
*ppSid,
|
|
&cbSidLength,
|
|
*ppszDomain,
|
|
&cbDomainLength,
|
|
&su))
|
|
{
|
|
dwError = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
// Double check the returned SID
|
|
if (!IsValidSid(*ppSid))
|
|
{
|
|
dwError = GetLastError();
|
|
goto ErrorExit;
|
|
}
|
|
|
|
return dwError;
|
|
|
|
ErrorExit:
|
|
if (*ppSid)
|
|
{
|
|
LocalFree(*ppSid);
|
|
*ppSid = NULL;
|
|
}
|
|
if (*ppszDomain)
|
|
{
|
|
LocalFree(*ppszDomain);
|
|
*ppszDomain = NULL;
|
|
}
|
|
|
|
fprintf(stderr, "Error: No account information was found.\n", dwError);
|
|
|
|
return dwError;
|
|
}
|
|
|
|
|
|
DWORD
|
|
DumpAccessAllowedList(PACL pDacl)
|
|
{
|
|
WORD wIndex;
|
|
LPVOID pAce = NULL;
|
|
|
|
if (pDacl == NULL)
|
|
{
|
|
fprintf(stdout, "The Discretionary Access Control List (DACL) for this object is a NULL DACL. "
|
|
"This implies everyone has full access to this object. This NULL DACL could be"
|
|
"in place because the system is running on a filesystem which does not support "
|
|
"protected files (such as the FAT32 filesystem)"
|
|
"\n");
|
|
goto done;
|
|
}
|
|
|
|
fprintf(stdout, "Additional accounts and groups with access to the private key include:\n");
|
|
for (wIndex = 0; wIndex < pDacl->AceCount; wIndex++)
|
|
{
|
|
if (GetAce(pDacl, wIndex, &pAce))
|
|
{
|
|
// Should only be an access allowed ace, right?
|
|
if (((ACCESS_ALLOWED_ACE *)pAce)->Header.AceType ==
|
|
ACCESS_ALLOWED_ACE_TYPE)
|
|
{
|
|
DumpSid((BYTE *)&(((ACCESS_ALLOWED_ACE *)pAce)->SidStart));
|
|
}
|
|
}
|
|
}
|
|
|
|
done:
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
// Wrapper that performs common work around the list, add, and remove actions
|
|
// involving a private key for a specified certificate.
|
|
DWORD DoPrivateKeyAccessAction(ARGS *pArgs)
|
|
{
|
|
HCRYPTPROV hProv = NULL;
|
|
BOOL fFreeProv = FALSE;
|
|
HCERTSTORE hCertStore = NULL;
|
|
PCCERT_CONTEXT pCertContext = NULL;
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
PACL pDacl = NULL;
|
|
DWORD cbSD = 0;
|
|
|
|
// Assume args have already been verified.
|
|
hCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM,
|
|
0,
|
|
NULL,
|
|
CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG |
|
|
(pArgs->fUseLocalMachine ? CERT_SYSTEM_STORE_LOCAL_MACHINE :
|
|
CERT_SYSTEM_STORE_CURRENT_USER),
|
|
pArgs->pwszCertStore);
|
|
|
|
if (!hCertStore)
|
|
{
|
|
DWORD dwError = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
pCertContext = CertFindCertificateInStore(hCertStore,
|
|
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
|
|
0,
|
|
CERT_FIND_SUBJECT_STR,
|
|
(LPVOID) pArgs->pwszCertSubject,
|
|
NULL);
|
|
|
|
if (pCertContext)
|
|
{
|
|
BYTE *pSid = NULL;
|
|
LPSTR pszDomain = NULL;
|
|
|
|
// Let the user have a solid idea of which cert was matched.
|
|
fprintf(stdout, "Matching certificate:\n");
|
|
DumpCertInfo(pCertContext);
|
|
|
|
// Bogus command will fall through when processing the cert context.
|
|
if (pArgs->Command != ARGS_LIST_PRIVATE_KEY_ACCESS)
|
|
{
|
|
dwError = GetSidForAccount(pArgs->pszDomain,
|
|
pArgs->pszAccount,
|
|
&pSid,
|
|
&pszDomain);
|
|
}
|
|
|
|
if (ERROR_SUCCESS == dwError)
|
|
{
|
|
dwError = ProcessCertContext(pCertContext, pSid, pArgs->Command);
|
|
|
|
if (pSid)
|
|
LocalFree(pSid);
|
|
|
|
if (pszDomain)
|
|
LocalFree(pszDomain);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr, "Error: Unable to find or obtain a context for requested certificate\n\n");
|
|
dwError = ERROR_NOT_FOUND;
|
|
}
|
|
|
|
Cleanup:
|
|
if (pCertContext)
|
|
{
|
|
CertFreeCertificateContext(pCertContext);
|
|
}
|
|
if (hCertStore)
|
|
{
|
|
CertCloseStore(hCertStore, CERT_CLOSE_STORE_FORCE_FLAG);
|
|
}
|
|
return dwError;
|
|
}
|
|
|
|
|
|
// For a given cert, obtain the DACL associated with its private
|
|
// key and process the appropriate command.
|
|
DWORD
|
|
ProcessCertContext(PCCERT_CONTEXT pCertContext, BYTE *pSid, ARGTYPE eCommand)
|
|
{
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
DWORD dwKeySpec;
|
|
DWORD cbSD;
|
|
WORD wIndex;
|
|
LPVOID pAce = NULL;
|
|
PSECURITY_DESCRIPTOR pSD = NULL;
|
|
SECURITY_DESCRIPTOR sd;
|
|
HCRYPTPROV hProv = NULL;
|
|
BOOL fFreeProv = FALSE;
|
|
PACL pDacl = NULL;
|
|
PACL pNewDacl = NULL;
|
|
BOOL fPresent = FALSE;
|
|
BOOL fDefault = FALSE;
|
|
|
|
if (!pCertContext)
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
// Get the CSP for the cert context.
|
|
// The client must be the owner to do this.
|
|
if (CryptAcquireCertificatePrivateKey(pCertContext,
|
|
CRYPT_ACQUIRE_USE_PROV_INFO_FLAG |
|
|
CRYPT_ACQUIRE_SILENT_FLAG,
|
|
NULL,
|
|
&hProv,
|
|
&dwKeySpec,
|
|
&fFreeProv))
|
|
{
|
|
// Grab the DACL ACE list
|
|
if (CryptGetProvParam(hProv,
|
|
PP_KEYSET_SEC_DESCR,
|
|
NULL,
|
|
&cbSD,
|
|
DACL_SECURITY_INFORMATION))
|
|
{
|
|
pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR, cbSD);
|
|
if (pSD)
|
|
{
|
|
if (CryptGetProvParam(hProv,
|
|
PP_KEYSET_SEC_DESCR,
|
|
(BYTE *)pSD,
|
|
&cbSD,
|
|
DACL_SECURITY_INFORMATION))
|
|
{
|
|
if (!GetSecurityDescriptorDacl(pSD,
|
|
&fPresent,
|
|
&pDacl,
|
|
&fDefault) && fPresent)
|
|
{
|
|
dwError = GetLastError();
|
|
fprintf(stderr, "Error: Failed to obtain access list private key\n\n");
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dwError = GetLastError();
|
|
fprintf(stderr, "Error: Failed to obtain security information for private key\n\n");
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dwError = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr, "Error: Failed to obtain security descriptor for private key\n\n");
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr, "Error: Access was not successfully obtained for the private key.\n");
|
|
fprintf(stderr, " This can only be done by the user who installed the certificate.\n\n");
|
|
goto Cleanup;
|
|
}
|
|
|
|
switch (eCommand)
|
|
{
|
|
case ARGS_ADD_PRIVATE_KEY_ACCESS:
|
|
case ARGS_REMOVE_PRIVATE_KEY_ACCESS:
|
|
|
|
if (pDacl == NULL)
|
|
{
|
|
fprintf(stdout, "OPERATION FAILED\n"
|
|
"The Discretionary Access Control List (DACL) for this object is a NULL DACL. "
|
|
"This implies everyone has full access to this object. This NULL DACL could be"
|
|
"in place because the system is running on a filesystem which does not support "
|
|
"protected files (such as the FAT32 filesystem)"
|
|
"\n");
|
|
dwError = ERROR_SUCCESS;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (eCommand == ARGS_ADD_PRIVATE_KEY_ACCESS)
|
|
dwError = AddPrivateKeyAccess(pDacl, pSid, &pNewDacl);
|
|
else
|
|
dwError = RemovePrivateKeyAccess(pDacl, pSid);
|
|
|
|
// If successful, update the DACL in the security descriptor
|
|
if (ERROR_SUCCESS == dwError)
|
|
{
|
|
SECURITY_DESCRIPTOR_CONTROL sdControl;
|
|
DWORD dwRevision;
|
|
|
|
if (!GetSecurityDescriptorControl(pSD, &sdControl, &dwRevision))
|
|
{
|
|
dwError = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
// initialize a new security descriptor.
|
|
if(!InitializeSecurityDescriptor(&sd, dwRevision))
|
|
{
|
|
dwError = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
// add the ACL to the security descriptor.
|
|
if(!SetSecurityDescriptorDacl(&sd,
|
|
TRUE,
|
|
pNewDacl ? pNewDacl : pDacl,
|
|
FALSE))
|
|
{
|
|
dwError = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Set descriptor control.
|
|
// API Helper for this exists only on Win2K and up.
|
|
MySetSecurityDescriptorControl(&sd, SE_DACL_PROTECTED, SE_DACL_PROTECTED);
|
|
|
|
if (!IsValidSecurityDescriptor(&sd))
|
|
{
|
|
dwError = ERROR_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if(!CryptSetProvParam(hProv,
|
|
PP_KEYSET_SEC_DESCR,
|
|
(BYTE*)&sd,
|
|
DACL_SECURITY_INFORMATION))
|
|
{
|
|
dwError = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ARGS_LIST_PRIVATE_KEY_ACCESS:
|
|
DumpAccessAllowedList(pDacl);
|
|
|
|
// Don't care about reporting an error here when dumping the list.
|
|
dwError = ERROR_SUCCESS;
|
|
break;
|
|
|
|
default:
|
|
// should never be here...do nothing
|
|
break;
|
|
}
|
|
|
|
Cleanup:
|
|
if (ERROR_SUCCESS != dwError)
|
|
{
|
|
fprintf(stderr,
|
|
"Error: Unable to update security info for key container, error = 0x%X\n\n",
|
|
dwError);
|
|
}
|
|
if (fFreeProv && hProv)
|
|
{
|
|
CryptReleaseContext(hProv, 0);
|
|
}
|
|
if (pSD)
|
|
{
|
|
LocalFree(pSD);
|
|
}
|
|
return dwError;
|
|
}
|
|
|
|
|
|
DWORD
|
|
AddPrivateKeyAccess(PACL pDacl, BYTE *pSid, PACL *ppNewDacl)
|
|
{
|
|
WORD wIndex;
|
|
BOOL fFound = FALSE;
|
|
LPVOID pAce = NULL;
|
|
|
|
for (wIndex = 0; wIndex < pDacl->AceCount; wIndex++)
|
|
{
|
|
if (GetAce(pDacl, wIndex, &pAce))
|
|
{
|
|
// Should only be an access allowed ace, right?
|
|
if (((ACCESS_ALLOWED_ACE *)pAce)->Header.AceType ==
|
|
ACCESS_ALLOWED_ACE_TYPE)
|
|
{
|
|
if (EqualSid((BYTE *)&(((ACCESS_ALLOWED_ACE *)pAce)->SidStart), pSid))
|
|
{
|
|
fprintf(stdout, "Private key access has already been granted for account:\n");
|
|
DumpSid((BYTE *)&(((ACCESS_ALLOWED_ACE *)pAce)->SidStart));
|
|
fFound = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
|
|
if (!fFound)
|
|
{
|
|
fprintf(stdout, "Granting private key access for account:\n");
|
|
DumpSid(pSid);
|
|
|
|
if (!AddAccessAllowedAce(pDacl, ACL_REVISION, KEY_ALL_ACCESS, pSid))
|
|
{
|
|
dwError = GetLastError();
|
|
if (ERROR_ALLOTTED_SPACE_EXCEEDED == dwError)
|
|
{
|
|
// Not enough space, so allocate a new dacl,
|
|
// and copy data from the old acl and append new ace.
|
|
WORD wAclSize = pDacl->AclSize + (WORD)GetLengthSid(pSid) +
|
|
sizeof(ACCESS_ALLOWED_OBJECT_ACE);
|
|
|
|
dwError = ERROR_SUCCESS;
|
|
|
|
// Allocate dacl + sizeof new sid + sizeof largest ace
|
|
*ppNewDacl = (PACL) LocalAlloc(LPTR, wAclSize);
|
|
|
|
if (NULL == *ppNewDacl)
|
|
{
|
|
dwError = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
if (!InitializeAcl (*ppNewDacl, wAclSize, pDacl->AclRevision))
|
|
{
|
|
dwError = GetLastError();
|
|
LocalFree(*ppNewDacl);
|
|
*ppNewDacl = NULL;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
// Copy all the original ACEs
|
|
for (wIndex = 0; wIndex < pDacl->AceCount; wIndex++)
|
|
{
|
|
if (GetAce(pDacl, wIndex, &pAce))
|
|
{
|
|
if (((ACCESS_ALLOWED_ACE *)pAce)->Header.AceType ==
|
|
ACCESS_ALLOWED_ACE_TYPE)
|
|
{
|
|
if (!AddAccessAllowedAce(*ppNewDacl,
|
|
ACL_REVISION,
|
|
((ACCESS_ALLOWED_ACE *)pAce)->Mask,
|
|
(BYTE *)&(((ACCESS_ALLOWED_ACE *)pAce)->SidStart)))
|
|
{
|
|
dwError = GetLastError();
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Should only contain access allowed ACEs,
|
|
// but we'll handle anyway.
|
|
if (!AddAccessDeniedAce(*ppNewDacl,
|
|
ACL_REVISION,
|
|
((ACCESS_DENIED_ACE *)pAce)->Mask,
|
|
(BYTE *)&(((ACCESS_DENIED_ACE *)pAce)->SidStart)))
|
|
{
|
|
dwError = GetLastError();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dwError = GetLastError();
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Check for any errors
|
|
if (wIndex >= pDacl->AceCount)
|
|
{
|
|
// Try again
|
|
if (!AddAccessAllowedAce(*ppNewDacl, ACL_REVISION, KEY_ALL_ACCESS, pSid))
|
|
{
|
|
dwError = GetLastError();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LocalFree(*ppNewDacl);
|
|
*ppNewDacl = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ErrorExit:
|
|
if (ERROR_SUCCESS != dwError)
|
|
{
|
|
fprintf(stderr, "Error: Failed to grant access with error code 0x%X\n\n", dwError);
|
|
}
|
|
return dwError;
|
|
}
|
|
|
|
|
|
DWORD
|
|
RemovePrivateKeyAccess(PACL pDacl, BYTE *pSid)
|
|
{
|
|
WORD wIndex;
|
|
LPVOID pAce = NULL;
|
|
BOOL fFound = FALSE;
|
|
|
|
for (wIndex = 0; wIndex < pDacl->AceCount; wIndex++)
|
|
{
|
|
if (GetAce(pDacl, wIndex, &pAce))
|
|
{
|
|
// Should only be an access allowed ace, right?
|
|
if (((ACCESS_ALLOWED_ACE *)pAce)->Header.AceType ==
|
|
ACCESS_ALLOWED_ACE_TYPE)
|
|
{
|
|
if (EqualSid((BYTE *)&(((ACCESS_ALLOWED_ACE *)pAce)->SidStart), pSid))
|
|
{
|
|
fprintf(stdout, "Removing private key access for account:\n");
|
|
DumpSid((BYTE *)&(((ACCESS_ALLOWED_ACE *)pAce)->SidStart));
|
|
if (!DeleteAce(pDacl, wIndex))
|
|
{
|
|
// Break on error
|
|
DWORD dwError = GetLastError();
|
|
fprintf(stderr,
|
|
"Error: Failed to remove access with error code 0x%X\n\n",
|
|
dwError);
|
|
return dwError;
|
|
}
|
|
fFound = TRUE; // Keep going? Can there be dupes?
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!fFound)
|
|
{
|
|
fprintf(stderr, "Account already does not have access to private key.\n\n");
|
|
}
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
// Wrapper to only set on Win2K and up because the API
|
|
// doesn't exist on NT4
|
|
BOOL
|
|
MySetSecurityDescriptorControl(
|
|
PSECURITY_DESCRIPTOR pSD,
|
|
SECURITY_DESCRIPTOR_CONTROL ControlBitsOfInterest,
|
|
SECURITY_DESCRIPTOR_CONTROL ControlBitsToSet
|
|
)
|
|
{
|
|
SETSECURITYDESCRIPTORCONTROL pfnSetSecurityDescriptorControl = NULL;
|
|
HMODULE hModule = NULL;
|
|
|
|
hModule = GetModuleHandle("advapi32.dll");
|
|
if (NULL != hModule)
|
|
{
|
|
pfnSetSecurityDescriptorControl = (SETSECURITYDESCRIPTORCONTROL)
|
|
GetProcAddress(hModule,
|
|
"SetSecurityDescriptorControl");
|
|
if (NULL != pfnSetSecurityDescriptorControl)
|
|
{
|
|
return (*pfnSetSecurityDescriptorControl)(pSD,
|
|
ControlBitsOfInterest,
|
|
ControlBitsToSet);
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
VOID
|
|
DumpCertInfo(PCCERT_CONTEXT pCertContext)
|
|
{
|
|
LPTSTR pszCertName = NULL;
|
|
DWORD cbCertName = 0;
|
|
|
|
if (!pCertContext)
|
|
return;
|
|
|
|
cbCertName = CertNameToStr(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
|
|
&pCertContext->pCertInfo->Subject,
|
|
CERT_X500_NAME_STR | CERT_NAME_STR_CRLF_FLAG,
|
|
NULL,
|
|
0);
|
|
|
|
pszCertName = (LPTSTR) LocalAlloc(LPTR, cbCertName * sizeof(TCHAR) + 1);
|
|
if (!pszCertName)
|
|
return;
|
|
|
|
*pszCertName = TEXT('\0');
|
|
|
|
if (CertNameToStr(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
|
|
&pCertContext->pCertInfo->Subject,
|
|
CERT_X500_NAME_STR | CERT_NAME_STR_CRLF_FLAG | CERT_NAME_STR_REVERSE_FLAG,
|
|
pszCertName,
|
|
cbCertName))
|
|
{
|
|
fprintf(stdout, "%s\n\n", pszCertName);
|
|
}
|
|
}
|
|
|
|
|
|
// Does quick and dirty check for a root cert by testing
|
|
// the issuer and issued to fields.
|
|
BOOL
|
|
CheckForRootCert(PCCERT_CONTEXT pCertContext)
|
|
{
|
|
TCHAR szIssuer[1024] = TEXT("");
|
|
TCHAR szIssuedTo[1024] = TEXT("");
|
|
DWORD cbCertName = 1024;
|
|
|
|
if (!pCertContext)
|
|
return FALSE;
|
|
|
|
CertGetNameString(pCertContext,
|
|
CERT_NAME_SIMPLE_DISPLAY_TYPE,
|
|
CERT_NAME_ISSUER_FLAG,
|
|
NULL,
|
|
szIssuer,
|
|
cbCertName);
|
|
|
|
CertGetNameString(pCertContext,
|
|
CERT_NAME_SIMPLE_DISPLAY_TYPE,
|
|
0,
|
|
NULL,
|
|
szIssuedTo,
|
|
cbCertName);
|
|
|
|
return (lstrcmp(szIssuer, szIssuedTo) == 0) ? TRUE : FALSE;
|
|
}
|
|
|
|
|
|
int __cdecl main (int argc, char **argv)
|
|
{
|
|
ARGS Args;
|
|
DWORD dwErr = ERROR_SUCCESS;
|
|
|
|
fprintf (stdout,
|
|
"Microsoft (R) WinHTTP Certificate Configuration Tool\n"
|
|
"Copyright (C) Microsoft Corporation 2001.\n\n"
|
|
);
|
|
|
|
// Discard program argument
|
|
argv++;
|
|
argc--;
|
|
|
|
ParseArguments(argc, argv, &Args);
|
|
|
|
switch (Args.Command)
|
|
{
|
|
case ARGS_IMPORT_PFX:
|
|
|
|
dwErr = ImportPFXFile(&Args);
|
|
break;
|
|
|
|
case ARGS_ADD_PRIVATE_KEY_ACCESS:
|
|
case ARGS_REMOVE_PRIVATE_KEY_ACCESS:
|
|
case ARGS_LIST_PRIVATE_KEY_ACCESS:
|
|
|
|
dwErr = DoPrivateKeyAccessAction(&Args);
|
|
break;
|
|
|
|
case ARGS_HELP:
|
|
default:
|
|
fprintf(stderr,
|
|
"Usage:\n\n"
|
|
" winhttpcertcfg [-?] : To view help information\n\n"
|
|
" winhttpcertcfg [-i PFXFile | -g | -r | -l]\n"
|
|
" [-a Account] [-c CertStore] [-s SubjectStr] [-p PFXPassword]\n\n"
|
|
"Note:\n\n"
|
|
" The user must have sufficient privileges to use this tool,\n"
|
|
" which likely requires the user to be an administrator and\n"
|
|
" the same user who installed the client certificate, if it is\n"
|
|
" already installed.\n\n"
|
|
"Options:\n\n"
|
|
" To list accounts which have access to the private key for\n"
|
|
" specified certificate:\n"
|
|
" winhttpcertcfg -l -c CertLocation -s SubjectStr\n\n"
|
|
" To grant access to private key for an account with\n"
|
|
" specified certificate that is already installed:\n"
|
|
" winhttpcertcfg -g -c CertLocation -s SubjectStr -a Account\n\n"
|
|
" To import a certificate plus private key from a PFX file:\n"
|
|
" winhttpcertcfg -i PFXFile -c CertLocation\n\n"
|
|
" To remove access to private key for an account with\n"
|
|
" specified certificate:\n"
|
|
" winhttpcertcfg -r -c CertLocation -s SubjectStr -a Account\n\n"
|
|
"Description of secondary options:\n\n"
|
|
" -c LOCAL_MACHINE|CURRENT_USER\\CertStore\n\n"
|
|
" Use LOCAL_MACHINE or CURRENT_USER to designate which\n"
|
|
" registry branch to use for the location. The\n"
|
|
" certificate store can be any installed on the machine.\n"
|
|
" Typical examples are MY, Root, and TrustedPeople.\n\n"
|
|
" -a Account\n\n"
|
|
" User account on the machine being configured. This could\n"
|
|
" be a local machine or domain account, such as:\n"
|
|
" IWAM_TESTMACHINE, TESTUSER, or TESTDOMAIN\\DOMAINUSER.\n\n"
|
|
" -s SubjectStr\n\n"
|
|
" Case-insensitive search string for finding the first\n"
|
|
" enumerated certificate with a subject name that contains\n"
|
|
" this substring.\n\n"
|
|
" -p PFXPassword\n\n"
|
|
" Password to use for importing the certificate and\n"
|
|
" private key. This option can only be used with -i.\n\n");
|
|
break;
|
|
}
|
|
|
|
if (Args.pwszPFXFile)
|
|
delete [] Args.pwszPFXFile;
|
|
if (Args.pwszCertStore)
|
|
delete [] Args.pwszCertStore;
|
|
if (Args.pwszCertSubject)
|
|
delete [] Args.pwszCertSubject;
|
|
if (Args.pwszPFXPassword)
|
|
delete [] Args.pwszPFXPassword;
|
|
|
|
return dwErr;
|
|
}
|