Leaked source code of windows server 2003
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.
 
 
 
 
 
 

744 lines
21 KiB

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 2001.
//
// File: cmdkey: cmdkey.cpp
//
// Contents: Main & command modules
//
// Classes:
//
// Functions:
//
// History: 07-09-01 georgema Created
//
//----------------------------------------------------------------------------
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wincred.h>
#include <wincrui.h>
#include "command.h"
#include "io.h"
#include "utils.h"
#include "consmsg.h"
#include "switches.h"
#ifdef VERBOSE
WCHAR szdbg[500]; // scratch char buffer for verbose output
#endif
/********************************************************************
GLOBAL VARIABLES, mostly argument parsing results
********************************************************************/
int returnvalue = 0;
// Switch flag model characters
#define VALIDSWITCHES 9 // A G L D HELP R U P S
// Define an array of character models and constants for the index into the array
// for each
WCHAR rgcS[] = {L"agld?rups"}; // referenced also in command.cpp
#define SWADD 0
#define SWGENERIC 1
#define SWLIST 2
#define SWDELETE 3
#define SWHELP 4
#define SWSESSION 5
#define SWUSER 6
#define SWPASSWORD 7
#define SWCARD 8
// variables for these to avoid repeated function calls into the parser
BOOL fAdd = FALSE;
BOOL fSession = FALSE;
BOOL fCard = FALSE;
BOOL fGeneric = FALSE;
BOOL fDelete = FALSE;
BOOL fList = FALSE;
BOOL fUser = FALSE;
BOOL fNew = FALSE; // sets true for any cred-creating switch (asg)
WCHAR SessionTarget[]={L"*Session"};
// temp buffer used for composing output strings involving a marshalled username
WCHAR szUsername[CRED_MAX_USERNAME_LENGTH + 1]; // 513 wchars
/********************************************************************
Conversion routines from enumerated values to their string equivalents
String values are held in an array of 63 character max strings
********************************************************************/
#define SBUFSIZE 64
#define TYPECOUNT 5
#define MAPTYPE(x) (x >= TYPECOUNT ? 0 : x)
WCHAR TString[TYPECOUNT][SBUFSIZE + 1];
#define PERSISTCOUNT 4
#define PERTYPE(x) (x>=PERSISTCOUNT ? 0 : x)
WCHAR PString[PERSISTCOUNT][SBUFSIZE + 1];
// Preload some strings from the application resources into arrays to be used by the
// application. These strings describe some enumerated DWORD values.
BOOL
AppInit(void)
{
// Allocate 2K WCHAR buffer to be used for string composition
if (NULL == szOut)
{
szOut = (WCHAR *) malloc((STRINGMAXLEN + 1) * sizeof(WCHAR));
if (NULL == szOut) return FALSE;
}
// Preload string from resources into buffers allocated on the stack as an array
// Total array size is 9 x 65 WCHARs = 1190 bytes
wcsncpy(TString[0],ComposeString(MSG_TYPE0),SBUFSIZE);
TString[0][SBUFSIZE] = 0;
wcsncpy(TString[1],ComposeString(MSG_TYPE1),SBUFSIZE);
TString[1][SBUFSIZE] = 0;
wcsncpy(TString[2],ComposeString(MSG_TYPE2),SBUFSIZE);
TString[2][SBUFSIZE] = 0;
wcsncpy(TString[3],ComposeString(MSG_TYPE3),SBUFSIZE);
TString[3][SBUFSIZE] = 0;
wcsncpy(TString[4],ComposeString(MSG_TYPE4),SBUFSIZE);
TString[4][SBUFSIZE] = 0;
wcsncpy(PString[0],ComposeString(MSG_PERSIST0),SBUFSIZE);
PString[0][SBUFSIZE] = 0;
wcsncpy(PString[1],ComposeString(MSG_PERSIST1),SBUFSIZE);
PString[1][SBUFSIZE] = 0;
wcsncpy(PString[2],ComposeString(MSG_PERSIST2),SBUFSIZE);
PString[2][SBUFSIZE] = 0;
wcsncpy(PString[3],ComposeString(MSG_PERSIST3),SBUFSIZE);
PString[3][SBUFSIZE] = 0;
if (PString[3][0] == 0) return FALSE;
return TRUE;
}
WCHAR
*TypeString(DWORD dwType)
{
return TString[MAPTYPE(dwType)];
}
WCHAR
*PerString(DWORD dwType)
{
return PString[PERTYPE(dwType)];
}
/********************************************************************
Get operating modes
(Code stolen from Credui:: CredUIApiInit()
********************************************************************/
#define MODEPERSONAL 1
#define MODESAFE 2
#define MODEDC 4
DWORD GetOSMode(void)
{
DWORD dwMode = 0;
// Check for Personal SKU:
OSVERSIONINFOEXW versionInfo;
versionInfo.dwOSVersionInfoSize = sizeof OSVERSIONINFOEXW;
if (GetVersionEx(reinterpret_cast<OSVERSIONINFOW *>(&versionInfo)))
{
if ((versionInfo.wProductType == VER_NT_WORKSTATION) &&
(versionInfo.wSuiteMask & VER_SUITE_PERSONAL))
dwMode |= MODEPERSONAL;
if (versionInfo.wProductType == VER_NT_DOMAIN_CONTROLLER)
dwMode |= MODEDC;
}
// Check for safe mode:
HKEY key;
if (RegOpenKeyEx(
HKEY_LOCAL_MACHINE,
L"SYSTEM\\CurrentControlSet\\Control\\SafeBoot\\Option",
0,
KEY_READ,
&key) == ERROR_SUCCESS)
{
if (RegQueryValueEx(
key,
L"OptionValue",
NULL,
NULL,
NULL,
NULL) == ERROR_SUCCESS)
{
dwMode |= MODESAFE;
}
RegCloseKey(key);
}
#ifdef VERBOSE
if (dwMode & MODEPERSONAL) OutputDebugString(L"CMDKEY: OS MODE - PERSONAL\n");
if (dwMode & MODEDC) OutputDebugString(L"CMDKEY: OS MODE - DOMAIN CONTROLLER\n");
if (dwMode & MODESAFE) OutputDebugString(L"CMDKEY: OS MODE - SAFE BOOT\n");
#endif
return dwMode;
}
/********************************************************************
Get allowable persistence value for the current logon session
taken from keymgr: krdlg.cpp
********************************************************************/
DWORD GetPersistenceOptions(DWORD dwPType) {
BOOL bResult;
DWORD i[CRED_TYPE_MAXIMUM];
DWORD dwCount = CRED_TYPE_MAXIMUM;
bResult = CredGetSessionTypes(dwCount,&i[0]);
if (!bResult) {
return CRED_PERSIST_NONE;
}
return i[dwPType];
}
/********************************************************************
COMMAND HANDLERS
********************************************************************/
DWORD DoAdd(INT argc, char **argv)
{
// Add a credential to the keyring.
// Note that the *Session credential is created in this routine, as well, though it uses
// a different command line switch than /a.
// For the *Session credential, the persistence is changed to session persistence, and
// the targetname is always "*Session". When the targetname appears on the command
// line UI, however, it is replaced by "<dialup session>" to mimic the behavior of keymgr.
// these buffers needed if we have to prompt
WCHAR szUser[CREDUI_MAX_USERNAME_LENGTH + 1]; // 513 wchars
WCHAR szPass[CREDUI_MAX_PASSWORD_LENGTH + 1]; // 257 wchars
CREDENTIAL stCred;
DWORD Persist;
WCHAR *pT = NULL; // targetname pointer
BOOL fP = FALSE; // password switch present
BOOL fS = FALSE; // smartcard if /C
WCHAR *pU = CLPtr(SWUSER);
WCHAR *pP = CLPtr(SWPASSWORD);
DWORD dwErr = NO_ERROR;
BOOL IsPersonal;
szUser[0] = 0;
szPass[0] = 0;
IsPersonal = (GetOSMode() & MODEPERSONAL);
// Get default persistence value
if (IsPersonal)
{
if (fSession)
Persist = CRED_PERSIST_SESSION;
else
Persist = GetPersistenceOptions(CRED_TYPE_GENERIC);
}
else
{
Persist = GetPersistenceOptions(CRED_TYPE_DOMAIN_PASSWORD);
}
// Error if no saves allowed and not on personal
// In personal, error of add operation unless session flag present
if (
(CRED_PERSIST_NONE== Persist) ||
(IsPersonal & (!(fSession || fGeneric)))
)
{
PrintString(MSG_CANNOTADD);
StompCommandLine(argc,argv);
return -1; // special value -1 will suppress default error message generation
}
if (fGeneric)
{
pT = CLPtr(SWGENERIC);
}
else
{
pT = CLPtr(SWADD); // default targetname - may be overridden by generic or session switches
}
#ifdef VERBOSE
// Some logging for debug purposes
if (pT)
swprintf(szdbg,L"CMDKEY: Target = %s\n",pT);
else
swprintf(szdbg,L"CMDKEY: Target = NULL\n");
OutputDebugString(szdbg);
if (pU)
swprintf(szdbg,L"CMDKEY: User = %s\n",pU);
else
swprintf(szdbg,L"CMDKEY: User is NULL\n");
OutputDebugString(szdbg);
#endif
// Original username - may be modified by prompting
if (pU)
{
wcsncpy(szUser,pU,CREDUI_MAX_USERNAME_LENGTH);
szUser[CREDUI_MAX_USERNAME_LENGTH] = 0;
}
// Override target name for prompting purposes
ZeroMemory((void *)&stCred, sizeof(CREDENTIAL));
// Prompting block - enter if password or smartcard switch on the commandline
if (CLFlag(SWPASSWORD) || fCard)
{
if ((pP) && (!fCard))
{
stCred.CredentialBlobSize = wcslen(pP) * sizeof(WCHAR);
stCred.CredentialBlob = (BYTE *)pP;
}
else
{
// prompt using credui command line mode
BOOL fSave = TRUE;
DWORD retval = 0;
if (!fCard)
{
retval = CredUICmdLinePromptForCredentials(
pT,
NULL,
0,
szUser,
CREDUI_MAX_USERNAME_LENGTH,
szPass,
CREDUI_MAX_PASSWORD_LENGTH,
&fSave,
CREDUI_FLAGS_DO_NOT_PERSIST |
CREDUI_FLAGS_EXCLUDE_CERTIFICATES |
CREDUI_FLAGS_GENERIC_CREDENTIALS);
}
else
{
retval = CredUICmdLinePromptForCredentials(
pT,
NULL,
0,
szUser,
CREDUI_MAX_USERNAME_LENGTH,
szPass,
CREDUI_MAX_PASSWORD_LENGTH,
&fSave,
CREDUI_FLAGS_DO_NOT_PERSIST |
CREDUI_FLAGS_REQUIRE_SMARTCARD
);
}
if (0 != retval)
{
#if DBG
OutputDebugString(L"CMDKEY: CredUI prompt failed\n");
#endif
dwErr = GetLastError();
// dont need to stomp the cmdline, since the psw wasnt on it
return dwErr;
}
else
{
stCred.CredentialBlobSize = wcslen(szPass) * sizeof(WCHAR);
stCred.CredentialBlob = (BYTE *)szPass;
}
}
}
stCred.Persist = Persist;
stCred.TargetName = pT;
stCred.UserName = szUser;
stCred.Type = CRED_TYPE_DOMAIN_PASSWORD;
// Override type and or persistence as necessary
// Override the targetname only in the case of a *Session cred
// Note that generic takes precedence over smartcard
if (fCard)
{
stCred.Type = CRED_TYPE_DOMAIN_CERTIFICATE;
}
if (fGeneric)
{
stCred.Type = CRED_TYPE_GENERIC;
}
if (!CredWrite((PCREDENTIAL)&stCred,0))
{
dwErr = GetLastError();
}
else PrintString(MSG_ADDOK);
StompCommandLine(argc,argv);
SecureZeroMemory(szPass,sizeof(szPass));
return dwErr;
}
// List the creds currently on the keyring. Unlike keymgr, show generic creds as well, though
// they cannot be created with cmdkey in this version.
// NOTE: Generic creds will be created using the /g switch in lieu of /a, just as *Session creds
// are created by /s in lieu of /a.
DWORD DoList(void)
{
PCREDENTIALW *pstC;
DWORD dwCount = 0;
WCHAR sz[500];
WCHAR *pL = CLPtr(SWLIST);
if (pL)
{
szArg[0] = pL;
PrintString(MSG_LISTFOR);
}
else PrintString(MSG_LIST);
if (CredEnumerateW(pL,0,&dwCount,&pstC))
{
for (DWORD i=0;i<dwCount;i++)
{
//Print target: targetname
// type: type string
// username: username from cred
// blank line
PrintString(MSG_TARGETPREAMBLE);
if (!_wcsicmp(pstC[i]->TargetName,SessionTarget))
{
//swprintf(sz,L"Dialup Session Credential\n");
PrintString(MSG_DIALUP);
PutStdout(L"\n");
}
else
{
PutStdout(pstC[i]->TargetName);
PutStdout(L"\n");
}
szArg[0] = TypeString(pstC[i]->Type);
PrintString(MSG_LISTTYPE);
// if the username is NULL, don't show it. This will happen with incomplete RAS
// creds that have not been filled in by Kerberos yet.
if ((pstC[i]->UserName != NULL) &&
(wcslen(pstC[i]->UserName) != 0))
{
// UnMarshallUserName will do so only if it is marshalled. Otherwise will leave it alone
szArg[0] = UnMarshallUserName(pstC[i]->UserName);
PrintString(MSG_LISTNAME);
}
PutStdout(PerString(pstC[i]->Persist));
}
if (pstC) CredFree(pstC);
}
if (0 == dwCount)
{
PrintString(MSG_LISTNONE);
}
return NO_ERROR;
}
// Delete a cred named using the /d switch, or if the /s switch is used, the *Session cred.
DWORD DoDelete(void)
{
BOOL fOK = FALSE;
BOOL fSuccess = FALSE;
DWORD dwErr = -1;
DWORD dw2;
WCHAR *pD = CLPtr(SWDELETE);
// get args from cmd line
// /d:targetname
#ifdef VERBOSE
if (pD)
swprintf(szdbg,L"CMDKEY: Target = %s\n",pD);
else
swprintf(szdbg,L"CMDKEY: Target = NULL\n");
OutputDebugString(szdbg);
#endif
if (fSession)
{
pD = SessionTarget;
}
// remove all creds with this target name
// Any successful delete is success. If no success...
// Failure returned is the last failure found by any attempt, excluding parameter faults
fOK = CredDelete(pD,CRED_TYPE_DOMAIN_PASSWORD,0);
if (!fOK)
{
dw2 = GetLastError();
if ((dwErr != NO_ERROR) && (dw2 != ERROR_INVALID_PARAMETER)) dwErr = dw2;
}
else dwErr = NO_ERROR;
fOK = CredDelete(pD,CRED_TYPE_DOMAIN_VISIBLE_PASSWORD,0);
if (!fOK)
{
dw2 = GetLastError();
if ((dwErr != NO_ERROR) && (dw2 != ERROR_INVALID_PARAMETER)) dwErr = dw2;
}
else dwErr = NO_ERROR;
fOK = CredDelete(pD,CRED_TYPE_GENERIC,0);
if (!fOK)
{
dw2 = GetLastError();
if ((dwErr != NO_ERROR) && (dw2 != ERROR_INVALID_PARAMETER)) dwErr = dw2;
}
else dwErr = NO_ERROR;
fOK = CredDelete(pD,CRED_TYPE_DOMAIN_CERTIFICATE,0);
if (!fOK)
{
dw2 = GetLastError();
if ((dwErr != NO_ERROR) && (dw2 != ERROR_INVALID_PARAMETER)) dwErr = dw2;
}
else dwErr = NO_ERROR;
if (dwErr == NO_ERROR)
{
PrintString(MSG_DELETEOK);
}
return dwErr;
}
/********************************************************************
Command dispatcher and error handling
********************************************************************/
// Perform the switched command, and display the error returned by GetLastError() if the error
// is both nonzero and not -1.
void
DoCmdKey(INT argc,char **argv)
{
DWORD dwErr = 0;
if (fAdd)
{
dwErr = DoAdd(argc,argv);
}
else if (fDelete)
{
dwErr = DoDelete();
}
else if (fList)
{
dwErr = DoList();
}
// Common residual error handler
if (NO_ERROR != dwErr)
{
returnvalue = 1;
if (dwErr != -1)
{
void *pv = NULL;
PrintString(MSG_PREAMBLE);
if (0 != FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM,
0,dwErr,0,(LPTSTR)&pv,500,NULL))
{
PutStdout((WCHAR *)pv);
LocalFree(pv);
}
}
}
}
// show usage string
void
Usage(BOOL fBad)
{
if (fBad) PrintString(MSG_BADCOMMAND);
else PrintString(MSG_CREATES);
PrintString(MSG_USAGE);
return;
}
/********************************************************************
Entry point - argument parsing, validation and call to dispatcher
********************************************************************/
int __cdecl
main(int argc, char *argv[], char *envp[])
{
INT iArgs = 0; // arg count
BOOL fError = FALSE; // command line error detected
// load needed strings - fail if failed
if (!AppInit())
{
// catastrophic exit
if (szOut) free(szOut);
return 1;
}
//CLInit allocates memory - you must exit via CLUnInit once this call succeeds
if (!CLInit(VALIDSWITCHES,rgcS)) return 1;
CLSetMaxPrincipalSwitch(SWDELETE);
// Parse the command line - fail on duplicated switch
if (!CLParse())
{
PrintString(MSG_DUPLICATE);
returnvalue = 1;
goto bail;
}
// Two ways to get help - no args at all
if (1 == CLTokens())
{
Usage(0);
returnvalue = 1;
goto bail;
}
// Explicit call for help via /?
if (CLFlag(SWHELP))
{
Usage(0);
returnvalue = 1;
goto bail;
}
// Detect extraneous switches - bail if found
#ifdef PICKY
if (CLExtra())
{
Usage(1);
returnvalue = 1;
goto bail;
}
#endif
// Set variables
fAdd = CLFlag(SWADD);
fCard = CLFlag(SWCARD);
fDelete = CLFlag(SWDELETE);
fGeneric = CLFlag(SWGENERIC);
fList = CLFlag(SWLIST);
fSession = CLFlag(SWSESSION);
fUser = CLFlag(SWUSER);
// Command line has been parsed - now look for interactions and missing necessities
// You have to be doing something - test for no principal command
if (CLGetPrincipalSwitch() < 0)
{
// firstcommand value will still be -1 - handled by default below
fError = TRUE;
}
// Weed out illegal combinations, missing arguments to switches where required
if (!fError && fAdd)
{
// need a name arg for this one in its native form
if (!CLPtr(SWADD)) fError = TRUE;
// no contradictory switches
if (fDelete || fList || fGeneric || fSession ) fError = TRUE;
}
if (!fError && fDelete)
{
//need a name arg unless the session switch is on the line
if ((!fSession) &&(!CLPtr(SWDELETE))) fError = TRUE;
//disallow target arg with session switch - success is ambiguous
if ((fSession) && (CLPtr(SWDELETE))) fError = TRUE;
// no contradictory switches
if (fAdd || fList || fGeneric || fUser ) fError = TRUE;
}
if (!fError && fList)
{
// no contradictory switches
if (fDelete || fAdd || fGeneric ||fUser || fSession) fError = TRUE;
}
// The generic flag replaces the add flag, using a different cred type.
// Do not allow it with the Add flag, and insist on a command argument
if (!fError && fGeneric)
{
if (!CLPtr(SWGENERIC)) fError = TRUE;
// no contradictory switches
if (fAdd) fError = TRUE;
// generic cred is an add operation
if (!fError) fAdd = TRUE;
}
// Display help for what we think is the operation the user attempted
// First announce that parameters were bad, then show help
if (fError)
{
PrintString(MSG_BADCOMMAND);
switch(CLGetPrincipalSwitch())
{
case SWADD:
PrintString(MSG_USAGEA);
break;
case SWGENERIC:
PrintString(MSG_USAGEG);
break;
case SWLIST:
PrintString(MSG_USAGEL);
break;
case SWDELETE:
PrintString(MSG_USAGED);
break;
default:
PrintString(MSG_USAGE);
break;
}
goto bail;
}
// Must specify a user for any add operation
// If the smartcard switch is supplied, the username may be absent
if (fAdd)
{
if ((!CLPtr(SWUSER)) && (!fCard))
{
PrintString(MSG_NOUSER);
returnvalue = 1;
goto bail;
}
}
DoCmdKey(argc,argv); // Execute the command action
bail:
CLUnInit();
if (NULL != szOut) free(szOut);
return returnvalue;
}