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.
1 lines
23 KiB
1 lines
23 KiB
// ===========================================================================
// UAMUtils.cp © 1997 Microsoft Corp. All rights reserved.
// ===========================================================================
// General utilities used by the Microsoft User Authentication Method.
//
// ===========================================================================
#ifdef UAM_TARGET_CARBON
#include <Carbon/Carbon.h>
#include <CoreServices/CoreServices.h>
#else
#include <OpenTransport.h>
#include <OpenTptLinks.h>
#include <OpenTptConfig.h>
#endif
#include <String.h>
#include <stdio.h>
#include <ctype.h>
#include <macstrsafe.h>
#include "UAMUtils.h"
#include "UAMNetwork.h"
#include "UAMDialogs.h"
#include "UAMDebug.h"
// ---------------------------------------------------------------------------
// ¥ UAM_CToPStr()
// ---------------------------------------------------------------------------
// In carbon, c2pstr is not supported. Roll our own.
//
// This should usually be called using the _c2pstr macro.
StringPtr UAM_CToPStr(char* ioStr)
{
if (ioStr != nil)
{
UInt8 theLen = strlen(ioStr);
BlockMoveData(ioStr, ioStr+1, theLen);
ioStr[0] = (char)theLen;
}
return((StringPtr)ioStr);
}
// ---------------------------------------------------------------------------
// ¥ UAM_PToCStr()
// ---------------------------------------------------------------------------
// In carbon, p2cstr is not supported. Roll our own.
//
// This should usually be called using the _p2cstr macro.
char* UAM_PToCStr(StringPtr ioStr)
{
if (ioStr != nil)
{
UInt8 theLen = PSTR_LENGTH(ioStr);
BlockMoveData(ioStr+1, ioStr, theLen);
ioStr[theLen] = 0;
}
return((char*)ioStr);
}
// ---------------------------------------------------------------------------
// ¥ UAM_PStrCopy()
// ---------------------------------------------------------------------------
// Custom routine for copying pascal style strings. Copies inSrcString into
// inDestString.
//
void UAM_PStrCopy(const Str255 inSrcString, StringPtr inDestString, SInt16 inMaxLength)
{
//
//01.16.02: Add check for string length. Default to 256 bytes max length.
//
Size theAmountToCopy = PSTR_LENGTH(inSrcString) + 1;
theAmountToCopy = (theAmountToCopy > inMaxLength) ? inMaxLength : theAmountToCopy;
BlockMove(inSrcString, inDestString, theAmountToCopy);
inDestString[0] = inSrcString[0];
}
// ---------------------------------------------------------------------------
// ¥ UAM_AppendPStr()
// ---------------------------------------------------------------------------
// Custom routine for appending one pascal style string to another.
// inAppendStr in pasted to the end of inBase. inDestSize if the maximum size
// the new string can be.
void UAM_AppendPStr(Str255 inBase, const Str255 inAppendStr, short inDestSize)
{
short charsToCopy = inAppendStr[0];
if ((inBase[0] + charsToCopy) > (inDestSize - 1)) {
charsToCopy = inDestSize - 1 - inBase[0];
}
BlockMove(inAppendStr + 1, inBase + inBase[0] + 1, charsToCopy);
inBase[0] += charsToCopy;
}
#pragma mark-
// ---------------------------------------------------------------------------
// ¥ UAM_GetUserName()
// ---------------------------------------------------------------------------
// Returns the default user name as set in the Sharing Setup/File Sharing
// dialogs. This would also be considered the old 'Chooser' name.
void UAM_GetUserName(StringPtr outUserName)
{
StringHandle theString = NULL;
outUserName[0] = 0;
theString = GetString(STR_ChooserUserName);
if (theString != NULL)
{
UAM_PStrCopy(*theString, outUserName);
}
}
// ---------------------------------------------------------------------------
// ¥ UAM_GetWorkStationName()
// ---------------------------------------------------------------------------
// Returns the workstation name as set in the Sharing Setup/File Sharing
// dialogs.
void UAM_GetWorkStationName(Str255 outWSName)
{
StringHandle theString = NULL;
Str255 theAddress;
//
//We assume failure here, if it fails, the user hasn't entered
//a workstation name. Most likely a missconfiguration.
//
outWSName[0] = 0;
theString = GetString(STR_Sys7WorkStationName);
if ((theString != NULL) && (PSTR_LENGTH(*theString) > 0))
{
HLock((Handle)theString);
UAM_PStrCopy(*theString, outWSName);
HUnlock((Handle)theString);
}
else
{
//
//No name exists in the resource or the resource does not
//exist.
//
UAM_PStrCopy(PSTR_DEFAULT_WORKSTATION_NAME, outWSName);
}
#ifndef UAM_TARGET_68K
OSStatus theStatus;
//
//For PPC and Carbon, we want to append the MAC address to the
//workstation name. This gives us better identification on the
//net for Mac clients.
//
theStatus = UAM_GetEthernetAddress(theAddress);
if (OT_SUCCESS(theStatus))
{
UAM_AppendPStr(outWSName, "\p(", sizeof(Str255));
UAM_AppendPStr(outWSName, theAddress, sizeof(Str255));
UAM_AppendPStr(outWSName, "\p)", sizeof(Str255));
}
else
{
DbgPrint_((DBGBUFF, "UAM_GetEthernetAddress() failed, error = %d", (int)theStatus));
}
#endif //UAM_TARGET_68K
}
// ---------------------------------------------------------------------------
// ¥ UAM_AFPClientSupportsOurUAM
// ---------------------------------------------------------------------------
// Returns TRUE if the client we're on is running AppleShare Client 3.8
// or later.
Boolean UAM_AFPClientSupportsOurUAM(void)
{
short theCfgWord;
short theVersion = UAM_AppleShareVersion(&theCfgWord);
if (theVersion <= gestaltAFPClient3_7_2)
{
//
//This will never happen under MacOS X.
//
return(false);
}
return(true);
}
// ---------------------------------------------------------------------------
// ¥ UAM_VerifyServerInfo()
// ---------------------------------------------------------------------------
// Verify that the server info reply block the server returned to us is
// valid.
//
// NOTE: This function assumes that the string buffer is packed with the
// strings in the order they appear in the AFPSrvrInfo structure.
// Any change in order on the server must result in a change here
// as well.
//
// -> inServerInfo Server info reply block sent to us by the server.
//
// Returns: TRUE if we think the reply block is valid.
//
// The format of the ServerInfo block is as follows:
//
// struct AFPSrvrInfo {
// short fMachineOffset;
// short fVerCountOffset;
// short fUAMCountOffset;
// short fIconOffset;
// short fFlags;
// unsigned char fSrvrName[2];
// char variablelengthdata[0];
// };
//
#if 0
Boolean UAM_VerifyServerInfo(
IN AFPSrvrInfo* inReplyInfo
)
{
UInt32 theEndOfBuffer;
StringPtr theString;
//
//First, make sure none of the offset fields are not negative numbers. In the
//context of this UAM, we MUST have version UAM and machines strings as well.
//
if ( (inReplyInfo->fMachineOffset > 0) ||
(inReplyInfo->fVerCountOffset > 0) ||
(inReplyInfo->fUAMCountOffset > 0) ||
(inReplyInfo->fIconOffset > 0) )
{
//
//The server info block is corrupt or has been tampered with.
//
DbgPrint_((DBGBUFF, "Invalid (negative or zero) offsets in server info buffer"));
return(FALSE);
}
//
//We have to assume the SFM server will always send the buffer in the same
//order and that it will always fill in all the blocks for information.
//
//========================================================================
//MACHINE TYPE
//========================================================================
//
//The machine offset should always be the space just after the server name.
//
SInt16 theMachineOffset = ((UInt32)inReplyInfo) + inReplyInfo->fSrvrName[0] + 1;
if (theMachineOffset != inReplyInfo->fMachineOffset)
{
//
//Problem: the calculated offset doesn't match the one passed.
//
DbgPrint_((DBGBUFF, "Bad machine offset in server info buffer! (%d vs.%d)",
inReplyInfo->fMachineOffset,
theMachineOffset
));
return(FALSE);
}
//
//Now see if the correct machine type is returned.
//
theString = ((StringPtr)(((UInt32)inReplyInfo) + theMachineOffset));
if (!EqualString(theString, PSTR_NT_MACHINE_TYPE, true, true))
{
//
//This is not a Windows NT server so the buffer is corrupt.
//
DbgPrint_((DBGBUFF, "Bad machine type in server info buffer! (%s)", _p2cstr(theString)));
return(FALSE);
}
//========================================================================
//AFP VERSION
//========================================================================
//
//The AFP version offset will always follow the machine type offset string.
//
SInt16 theVersionOffset = PSTR_LENGTH(theString) + 1;
SInt16 theVersionCount = *(SInt16*)(((UInt32)inReplyInfo) + theVersionOffset);
//
//Make sure our calculated offset matches the one passed to us.
//
if (theVersionOffset != inReplyInfo->fVerCountOffset)
{
//
//Offset is incorrect so the buffer is corrupt.
//
DbgPrint_((DBGBUFF, "Bad afp version offset in server info buffer! (%d vs.%d)",
inReplyInfo->fVerCountOffset,
theVersionOffset
));
return(FALSE);
}
//
//We don't expect any more than 3 AFP versions from the server.
//
if ((theVersionCount < MINNUM_AFP_VERSIONS_SUPPORTED) ||
(theVersionCount > MAXNUM_AFP_VERSIONS_SUPPORTED) )
{
//
//Bad number of versions supported.
//
DbgPrint_((DBGBUFF, "Bad afp version count in server info buffer! (%d)",
theVersionCount
));
return(FALSE);
}
return(TRUE);
}
#endif
// ---------------------------------------------------------------------------
// ¥ UAM_GetSupportedUAMS()
// ---------------------------------------------------------------------------
// Returns a bitmap containing the UAMs supported on the server.
//
// -> inReplyInfo Pointer to reply info supplied by ASPGetStatus() call.
// <- outSupportedUAMS Bitmap containing supported UAMs:
// - Clear Text Password (Apple)
// - Guest (Apple and MS)
// - Microsoft V1.0
// - Microsoft V2.0
// - Microsoft V3.0
//
// As per Inside AppleTalk p.13-96, the supported UAM string variable list structure:
//
// |-------------------|
// | Count of UAMs |
// |-------------------|
// | |
// ¥ ¥
// ¥ UAM Strings ¥
// ¥ ¥
// | |
// |-------------------|
void UAM_GetSupportedUAMS(
IN UAMArgs* inUAMArgs,
IN StringPtr inAFPVersion,
OUT SInt32* ioSupported)
{
AFPSrvrInfo* theServerInfo = inUAMArgs->Opt.open.srvrInfo;
char* theString;
SInt16 theIndex;
SInt16 theUAMCount;
if ((ioSupported == NULL) || (inAFPVersion == NULL))
{
if (ioSupported != NULL)
{
*ioSupported = 0;
}
return;
}
theString = ((char *)theServerInfo) + theServerInfo->fUAMCountOffset;
theUAMCount = *theString;
//
//The first byte is the UAM count, so we must increment past it.
//
++theString;
//
//Initialize the return struct to all false.
//
*ioSupported = 0L;
for (theIndex = theUAMCount; theIndex > 0; theIndex--, theString += theString[0] + 1)
{
if (EqualString(PSTR_ClearTextLogin, (StringPtr)theString, false, false))
{
*ioSupported |= kClearTxtSupported;
continue;
}
if (EqualString(PSTR_GuestLogin, (StringPtr)theString, false, false))
{
*ioSupported |= kGuestSupported;
continue;
}
if (EqualString(PSTR_EncryptedLogin1_0, (StringPtr)theString, false, false))
{
*ioSupported |= kMSUAMSupported;
continue;
}
if (EqualString(PSTR_EncryptedLogin2_0, (StringPtr)theString, false, false))
{
*ioSupported |= kMSUAM_V2_Supported;
continue;
}
if (EqualString(PSTR_EncryptedLogin3_0, (StringPtr)theString, false, false))
{
*ioSupported |= kMSUAM_V3_Supported;
continue;
}
}
//
//02.08.02: We check to make sure the server is not lying to us regarding
//what auth method it supports. This will make a "downgrade" attack
//a little harder, but doesn't stop it completely.
//
if ((*ioSupported & kMSUAM_V2_Supported) == 0)
{
//
//The server is telling us that it is not a Win2K server and as a result
//does not support the new stronger NTLMv2 auth. method. Make some checks
//to see if it is lying to us.
//
#ifndef UAM_TARGET_CARBON
if ( (theServerInfo->fFlags & kSupportsTCPIP) ||
(inUAMArgs->Opt.open.srvrAddress->fAddressType == AF_INET) )
#else
if ( (theServerInfo->fFlags & kSupportsTCPIP) )
#endif
{
DbgPrint_((DBGBUFF, "SPOOF! TCP connection - forcing NTLMv2 auth!"));
//
//Gotcha! If the server supports TCP/IP connections then it
//is at least a Windows2000 server that supports NTLMv2.
//
*ioSupported |= kMSUAM_V2_Supported;
}
//
//Check the AFP version and see if we were spoofed somehow there.
//
//
//If the 4th character in the string is a digit (0-9) then we know
//we have AFP2.2 or higher as this is when Apple went from the
//AFPVersion X.X format to the AFPX.X format.
//
if (isdigit(inAFPVersion[4]))
{
DbgPrint_((DBGBUFF, "SPOOF! AFP2.2 or higher is supported - forcing NTLMv2 auth!"));
//
//Gotcha! Force NTLMv2 authorization since the server supports AFP2.2
//or higher.
//
*ioSupported |= kMSUAM_V2_Supported;
}
}
}
#pragma mark-
// ---------------------------------------------------------------------------
// ¥ UAM_VersionString
// ---------------------------------------------------------------------------
// Returns a string containing the version of this build.
void UAM_VersionString(Str32 outVersionString)
{
Handle theVersHandle;
long theVersion;
unsigned char theVer1, theVer2, theVer3, theRelStatus, thePrereleaseNum;
Str32 theTempStr;
outVersionString[0] = 0;
UAM_PStrCopy("\pv", outVersionString);
theVersHandle = Get1Resource('vers', 1);
if (theVersHandle)
{
theVersion = *((long *)(*theVersHandle));
ReleaseResource(theVersHandle);
theVer1 = ((char *)&theVersion)[0];
theVer1 = (((theVer1 & 0xF0) >> 4) * 10) + (theVer1 & 0x0F);
theVer2 = (((char *)&theVersion)[1] & 0xF0) >> 4;
theVer3 = (((char *)&theVersion)[1] & 0x0F);
theRelStatus = ((char *)&theVersion)[2];
thePrereleaseNum = ((char *)&theVersion)[3];
NumToString((long)theVer1, theTempStr);
UAM_AppendPStr(outVersionString, theTempStr, sizeof(Str32));
UAM_AppendPStr(outVersionString, "\p.", sizeof(Str32));
NumToString((long)theVer2, theTempStr);
UAM_AppendPStr(outVersionString, theTempStr, sizeof(Str32));
if (theVer3 != 0)
{
UAM_AppendPStr(outVersionString, "\p.", sizeof(Str32));
NumToString((long)theVer3, theTempStr);
UAM_AppendPStr(outVersionString, theTempStr, sizeof(Str32));
}
switch(theRelStatus)
{
case 0x20:
UAM_AppendPStr(outVersionString, "\pd", sizeof(Str32));
break;
case 0x40:
UAM_AppendPStr(outVersionString, "\pa", sizeof(Str32));
break;
case 0x60:
UAM_AppendPStr(outVersionString, "\pb", sizeof(Str32));
break;
default:
break;
}
if (theRelStatus != 0x80)
{
NumToString((long)thePrereleaseNum, theTempStr);
UAM_AppendPStr(outVersionString, theTempStr, sizeof(Str32));
}
}
}
// ---------------------------------------------------------------------------
// ¥ UAM_GetAFPVersionString
// ---------------------------------------------------------------------------
// Return the AFP version string and the default user name.
void UAM_GetAFPVersionString( AFPSrvrInfo *inInfo,
ClientUAMCallbackRec *inCallbacks,
Str32 ioAFPVersion,
Str32 ioDefaultUserName )
{
struct AFPClientInfo *theClientInfo = NULL;
short theIndex;
StringPtr theVersionBuf;
UInt32 theVersionBufSize;
Boolean theResult;
OSStatus theError;
ioAFPVersion[0] = 0;
ioDefaultUserName[0] = 0;
if ((inCallbacks) && (inInfo))
{
//
//Use the UAM callback GetClientInfo() to get the client info.
//
theError = UAM_CALLBACK_TWOPARAM(
inCallbacks->GetClientInfoUPP,
kGetClientInfoProcInfo,
kAFPClientInfo,
(ClientInfo **)&theClientInfo);
if (theError != noErr)
{
UAM_ReportError(theError);
return;
}
if (theClientInfo)
{
//
//Stuff the default user name in the return parameter.
//
UAM_PStrCopy(theClientInfo->fDefaultUserName, ioDefaultUserName);
//
//Go through the list of AFP versions supported on this client
//and try to find them in the SrvrInfoBuffer, first match
//succeeds.
//
theVersionBuf = (StringPtr)((UInt32)inInfo + inInfo->fVerCountOffset + 1);
theVersionBufSize = (inInfo->fUAMCountOffset - inInfo->fVerCountOffset) - 1;
for (theIndex = 0; theIndex < theClientInfo->fNumAFPVersions; theIndex++)
{
theResult = UAM_FindStringInBuffer(
theClientInfo->fAFPVersionStrs[theIndex],
theVersionBuf,
theVersionBufSize );
if (theResult)
{
UAM_PStrCopy(theClientInfo->fAFPVersionStrs[theIndex], ioAFPVersion);
return;
}
}
}
}
}
// ---------------------------------------------------------------------------
// ¥ UAM_FindStringInBuffer
// ---------------------------------------------------------------------------
// Finds a pascal string within a buffer.
Boolean UAM_FindStringInBuffer(StringPtr inString, StringPtr inBuffer, short inBufferSize)
{
short i = 0;
short theLen;
theLen = inBuffer[i];
while(TRUE)
{
if (memcmp(&inBuffer[i], inString, theLen + 1) != 0)
{
i += theLen + 1;
theLen = inBuffer[i];
if (i >= inBufferSize)
break;
}
else {
return(TRUE);
}
}
return(FALSE);
}
// ---------------------------------------------------------------------------
// ¥ UAM_AppleShareVersion()
// ---------------------------------------------------------------------------
// Returns the version of AppleShare running on the host computer.
short UAM_AppleShareVersion(short *upperWord)
{
long theResult;
OSErr theError;
theError = Gestalt(gestaltAFPClient, &theResult);
if (!theError)
{
if (upperWord != NULL) {
*upperWord = (theResult & gestaltAFPClientAttributeMask);
}
return(theResult & gestaltAFPClientVersionMask);
}
return(0);
}
// ---------------------------------------------------------------------------
// ¥ UAM_KeyDown()
// ---------------------------------------------------------------------------
// Returns TRUE if the passed keycode is currently held down.
Boolean UAM_KeyDown(Int16 inKeycode)
{
KeyMapByteArray theKeyMap;
#ifdef UAM_TARGET_CARBON
GetKeys((SInt32*)theKeyMap);
#else
GetKeys((UInt32*)theKeyMap);
#endif
return((theKeyMap[inKeycode>>3] >> (inKeycode & 7)) & 1);
}
// ---------------------------------------------------------------------------
// ¥ UAM_ExtractDomainName()
// ---------------------------------------------------------------------------
// Extracts a domain name from a user name entry of: domain\username
//
// outDomainName must be a buffer big enough to hold the found domain
// name or MAX_DOMAIN_NAME_LEN+1 in length.
//
// Returns: TRUE if a domain name was found.
//
Boolean UAM_ExtractDomainName(char* inUserName, char* outDomainName, SInt16 cbDomainName)
{
char* theTemp;
theTemp = strchr(inUserName, '\\');
if (theTemp)
{
strncpy(
outDomainName,
inUserName,
_min(cbDomainName, (theTemp-inUserName))
);
return(TRUE);
}
return(FALSE);
}
// ---------------------------------------------------------------------------
// ¥ UAM_ExtractUserName()
// ---------------------------------------------------------------------------
// Extracts a user name from a user name entry of: domain\username
//
// outUserName must be a buffer big enough to hold the found user
// name or MAX_USER_NAME_LEN+1 in length.
//
// Returns: TRUE if a user name was found.
//
Boolean UAM_ExtractUserName(char* inUserName, char* outUserName, SInt16 cbOutUserName)
{
char* theTemp;
char theTempUserName[64];
//
//We make a copy of the original user name so the caller can
//use the same buffer to receive the new actual user name.
//
StringCbCopy(theTempUserName, sizeof(theTempUserName), inUserName);
theTemp = strchr(theTempUserName, '\\');
if (theTemp)
{
StringCbCopy(outUserName, cbOutUserName, theTemp+1);
return(strlen(outUserName) > 0);
}
return(FALSE);
}
// ---------------------------------------------------------------------------
// ¥ UAM_GetEthernetAddress()
// ---------------------------------------------------------------------------
// Returns the hardware ethernet address by using OpenTransport.
//
// ****This function will crash if run on 68K machines!****
//
OSStatus UAM_GetEthernetAddress(Str255 outAddress)
{
OSStatus theStatus = kOTNoError;
UInt32 theIndex = 0;
EndpointRef theEndPoint;
OTPortRecord theDevicePortRec;
TBind theReturnInfo;
TBind theRequestInfo;
Address8022 theReturnAddr = {AF_8022, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 0x0000,
{0x00,0x00,0x00,0x00,0x00}};
Address8022 theAddress = {AF_8022, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 0x8888,
{0x00,0x00,0x00,0x00,0x00}};
Boolean fFoundAPort = TRUE;
theStatus = UAMInitOpenTransport(kInitOTForApplicationMask, NULL);
if (OT_SUCCESS(theStatus))
{
//
//Iterate through each OT port record for ethernet devices.
//
while(TRUE)
{
fFoundAPort = OTGetIndexedPort(&theDevicePortRec, theIndex);
if (!fFoundAPort)
{
break;
}
if ((theDevicePortRec.fCapabilities & kOTPortIsDLPI) &&
(theDevicePortRec.fCapabilities & kOTPortIsTPI) &&
(kOTEthernetDevice == OTGetDeviceTypeFromPortRef(theDevicePortRec.fRef)))
{
theEndPoint = UAMOTOpenEndpoint(
OTCreateConfiguration(theDevicePortRec.fPortName),
(OTOpenFlags)NULL,
NULL,
&theStatus
);
if (OT_SUCCESS(theStatus))
{
//
//Bind the endpoint so we can get the address info.
//
theRequestInfo.addr.buf = (UInt8*)&theAddress;
theRequestInfo.addr.len = 10;
theRequestInfo.addr.maxlen = 0;
theRequestInfo.qlen = 0;
theStatus = OTBind(theEndPoint, &theRequestInfo, NULL);
if (OT_SUCCESS(theStatus))
{
theReturnInfo.addr.buf = (UInt8*)&theReturnAddr;
theReturnInfo.addr.maxlen = 10;
theReturnInfo.qlen = 0;
theStatus = OTGetProtAddress(
theEndPoint,
&theReturnInfo,
NULL
);
if (OT_SUCCESS(theStatus))
{
char c1,c2;
Str15 theHex;
theHex[0] = 12;
for (Int16 i = 0; i < 6; i++)
{
c1 = ((theReturnAddr.fHWAddr[i] >> 4) & 0xF) + '0';
c2 = (theReturnAddr.fHWAddr[i] & 0x0F) + '0';
if (c1 > '9') c1 = c1 + ('A' - '9' - 1);
if (c2 > '9') c2 = c2 + ('A' - '9' - 1);
theHex[2*i+1] = c1;
theHex[2*i+2] = c2;
}
UAM_PStrCopy(theHex, outAddress);
break;
}
OTUnbind(theEndPoint);
}
OTCloseProvider(theEndPoint);
}
}
theIndex++;
}
}
UAMCloseOpenTransport(NULL);
return theStatus;
}
|