// =========================================================================== // UAMUtils.cp © 1997 Microsoft Corp. All rights reserved. // =========================================================================== // General utilities used by the Microsoft User Authentication Method. // // =========================================================================== #ifdef UAM_TARGET_CARBON #include #include #else #include #include #include #endif #include #include #include #include #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; }