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
39 KiB
1 lines
39 KiB
// ===========================================================================
// UAMNetwork.c © 1998-2001 Microsoft Corp. All rights reserved.
// ===========================================================================
// Networking functions for use by Microsoft User Authentication Method.
//
// There are several versions of authentication used by the UAM to an SFM
// server. Here is the matrix which shows which Microsoft AFP encryption
// version will be used depending on the server version.
//
// Server Version Auth Chng Pswd Notes
// -------------- ---- --------- ---------------
// NT 4.0 1.0 1.0 Weakest, LM Hash only
// Win2K 2.0 2.0 NTLMv2 auth, LM Hash for chgn pswd
// .NET 2.0 3.0 Strongest, using NTLMv2 for both
//
// ===========================================================================
#ifdef UAM_TARGET_CARBON
#include <CoreFoundation/CoreFoundation.h>
#include <Carbon/Carbon.h>
#endif
#include <macssp.h>
#include <String.h>
#include <macstrsafe.h>
#include "UAMMain.h"
#include "UAMEncrypt.h"
#include "UAMDebug.h"
#include "UAMNetwork.h"
#include "UAMUtils.h"
#include "UAMDLOGUtils.h"
#include "UAMDialogs.h"
#include "UAMKeychain.h"
#include "UAMPrefs.h"
unsigned char gCmdBuffer[kMaxAFPCommand];
unsigned char gReplyBuffer[kMaxAFPCommand];
MSUAMLoginReplyBlock gMSUAMReply;
extern Str32 gAFPVersion;
extern OTAddress *gServerAddress;
extern long gSupportedUAMs;
extern UInt32 gExpirationTime;
extern Str32 gServerName;
extern Boolean gGuestLogon;
extern DialogPtr gDialog;
extern UAM_PREFERENCES gUAMPreferences;
// ---------------------------------------------------------------------------
// ¥ UAM_MapCharactersIntoHostSet()
// ---------------------------------------------------------------------------
// Given a counted string, and a "host mapping table", do an in-place conversion
// of that string into the host character set. The table is construed to be
// of length 255 - StartingExtendedCharValue chars long, and a character for
// character conversion will be indicated for any chars in targetStr which
// are equal to or in excess of StartingExtendedCharValue.
Boolean UAM_MapCharactersIntoHostSet(char *szTarg, char *mappingTbl)
{
unsigned char c;
while (*szTarg)
{
if ((unsigned char)*szTarg >= (unsigned char)kStartingExtendedCharValue)
{
c = *(mappingTbl+ (unsigned char)*szTarg - kStartingExtendedCharValue);
if (c == kIllegalMappedExtChar) {
DbgPrint_((DBGBUFF, "Illegal mapping character"));
return(false);
}
else {
*szTarg = c;
}
}
szTarg++;
}
return(true);
}
#pragma mark-
// ---------------------------------------------------------------------------
// ¥ UAM_UAMLogin()
// ---------------------------------------------------------------------------
// This routine does the actual logging onto the server.
OSStatus UAM_UAMLogin(UAMArgs *inUAMArgs)
{
OSStatus theError = noErr;
Boolean theLoop = true;
CursHandle theCursor;
Assert_(inUAMArgs != NULL);
do
{
theCursor = GetCursor(watchCursor);
SetCursor(*theCursor);
if (gGuestLogon) {
theError = UAM_LoginGuest(inUAMArgs);
}
else {
SspDebugPrintHex(
(char*)inUAMArgs->Opt.pwDlg.userName,
PSTR_LENGTH(inUAMArgs->Opt.pwDlg.userName)+1
);
SspDebugPrintHex(
(char*)inUAMArgs->Opt.pwDlg.password,
strlen((char*)inUAMArgs->Opt.pwDlg.password)
);
//
//The password buffer in UAMArgs is always in C style form. The encryption
//routines expect it to be in pascal style, so we convert it before we
//send it on.
//
if (strlen((char*)inUAMArgs->Opt.pwDlg.password) != 0)
{
_c2pstr((char*)inUAMArgs->Opt.pwDlg.password);
}
theError = UAM_LoginMSUAM(inUAMArgs);
}
if (theError != noErr)
{
//
//For whatever reason, we couldn't log into the server, handle the most
//basic errors and try to logon again by presenting the login dialog
//again. Otherwise, exit...
//
//
//Make sure in debug that the password field is cleared in
//an error condition.
//
Assert_(PSTR_LENGTH(inUAMArgs->Opt.pwDlg.password) == 0);
//
//03.11.02 MJC: Only put up the error dialog for real errors.
//
if (theError != userCanceledErr) {
UAM_ReportError(theError);
}
UAM_CloseSession(inUAMArgs);
switch(theError)
{
//
//Standard uam error codes that may occur during logon when
//we will want to allow the user to try again.
//
//
case afpNTPasswordExpired:
case afpPwdExpiredErr:
case afpUserNotAuth:
case afpParmErr:
case afpNTAccountDisabled:
case afpNTInvalidWorkstation:
case afpNTInvalidLogonHours:
//
//We use this error to signal to the uam that a non-critical error
//has occured during login, most likely due to the users uam
//configuration.
//
case userCanceledErr:
if (MS_UAMPwdDialog(inUAMArgs) != noErr)
return(userCanceledErr);
break;
default:
theLoop = false;
theError = userCanceledErr;
break;
}
}
else
{
if ((gSupportedUAMs & (kMSUAM_V2_Supported | kMSUAM_V3_Supported)) && (!gGuestLogon))
{
//
//Check for password expiration at this point.
//
UInt32 theDaysTillExpiration = (((gExpirationTime / 60) / 60) / 24);
if (theDaysTillExpiration <= MINIMUM_DAYS_TILL_EXPIRATION)
{
//
//The password is going to expire within MINIMUM_DAYS_TILL_EXPIRATION,
//post the nofication dialog.
//
UAM_ChangePasswordNotificationDlg(theDaysTillExpiration);
}
}
if (UAM_KCAvailable())
{
//
//If the user is allowed to save their password and
//the keychain check box is checked, save the current
//credentials to the keychain.
//
#ifdef UAM_TARGET_CARBON
if (UAM_GetDialogControlValue(gDialog, DITEM_Keychain) > 0)
#else
if (UAM_GetCValue(gDialog, DITEM_Keychain) > 0)
#endif
{
theError = UAM_KCSavePassword(
inUAMArgs->Opt.auth.userName,
inUAMArgs->Opt.auth.password,
gServerName
);
if ((theError != noErr) && (theError != userCanceledErr))
{
if (theError == errKCDuplicateItem)
{
Int16 theResponse;
//
//A duplicate item exists, see if the user wants
//to replace it.
//
UAM_StandardAlert(
uamErr_KeychainEntryExistsMessage,
uamErr_KeychainEntryExistsExplanation,
&theResponse);
if (theResponse == kAlertStdAlertOKButton)
{
//
//The user asked us to replace the item. Try one
//more time to add the keychain item.
//
theError = UAM_KCDeleteItem(
inUAMArgs->Opt.auth.userName,
gServerName
);
if (theError == noErr)
{
theError = UAM_KCSavePassword(
inUAMArgs->Opt.auth.userName,
inUAMArgs->Opt.auth.password,
gServerName
);
if (theError != noErr)
{
//
//We errored out, nothing to do but report it.
//
UAM_ReportError(theError);
}
}
}
}
else
{
UAM_ReportError(theError);
}
//
//We do not want to pass back any keychain error codes to
//the AFP client!
//
theError = noErr;
}
}
}
//
//04.27.01: We don't need this buffer that contained the password
//in cleartext form anymore. Clear them for better security.
//03.02.02 MJC - Convert to use RtlSecureZeroMemory().
//
RtlSecureZeroMemory(
inUAMArgs->Opt.auth.password,
PSTR_LENGTH(inUAMArgs->Opt.pwDlg.password)+1
);
theLoop = false;
}
}while(theLoop);
return(theError);
}
#pragma mark-
// ---------------------------------------------------------------------------
// ¥ UAM_CloseSession()
// ---------------------------------------------------------------------------
// Close a session on the AFP server.
OSStatus UAM_CloseSession(UAMArgs *inUAMArgs)
{
OSStatus theError;
theError = UAM_CALLBACK_ONEPARAM(
inUAMArgs->callbacks->CloseSessionUPP,
kCloseSessionProcInfo,
inUAMArgs->sessionRefNum);
inUAMArgs->sessionRefNum = 0;
return(theError);
}
// ---------------------------------------------------------------------------
// ¥ UAM_OpenSession()
// ---------------------------------------------------------------------------
// Open a session on the AFP server.
OSStatus UAM_OpenSession( UAMArgs *inUAMArgs,
UAMMessage *inMessage,
unsigned char *inCmdBuffer,
UInt32 inCmdBufferSize,
unsigned char *inReplyBuffer,
UInt32 inReplyBufferSize )
{
OSStatus theError;
Assert_(inUAMArgs != NULL);
Assert_(inMessage != NULL);
Assert_(inCmdBuffer != NULL);
Assert_(gServerAddress != NULL);
//
//Note that inReplyBuffer can be null.
//
inMessage->commandCode = kOpenSession;
inMessage->cmdBuffer = inCmdBuffer;
inMessage->cmdBufferSize = inCmdBufferSize;
inMessage->replyBuffer = inReplyBuffer;
inMessage->replyBufferSize = inReplyBufferSize;
inMessage->completion = NULL;
inMessage->contextPtr = NULL;
theError = UAM_CALLBACK_THREEPARAM(
inUAMArgs->callbacks->OpenSessionUPP,
kOpenSessionProcInfo,
gServerAddress,
NULL,
inMessage);
//
//Even if theError == noErr, the parameter block's result
//param still might hold an error code!
//
return((theError == noErr) ? inMessage->result : theError);
}
// ---------------------------------------------------------------------------
// ¥ UAM_LoginGuest()
// ---------------------------------------------------------------------------
// Log into an AFP server as a guest.
OSStatus UAM_LoginGuest(UAMArgs *inUAMArgs)
{
Ptr theCmdPtr;
UInt32 theCmdSize;
OSStatus theError;
UAMMessage theMessage;
Assert_(inUAMArgs != NULL);
Assert_(PSTR_LENGTH(gAFPVersion) != 0);
theCmdPtr = (Ptr)&gCmdBuffer[0];
*theCmdPtr = kFPLogin;
theCmdPtr += sizeof(Byte);
StringPush_(gAFPVersion, (StringPtr)theCmdPtr);
StringPush_(PSTR_GuestLogin, (StringPtr)theCmdPtr);
theCmdSize = theCmdPtr - ((Ptr)&gCmdBuffer);
theError = UAM_OpenSession( inUAMArgs,
&theMessage,
gCmdBuffer,
theCmdSize,
NULL,
0 );
DbgPrint_((DBGBUFF, "OpenSession() returned %d in UAM_LoginGuest()", (int)theError));
if (theError == noErr) {
inUAMArgs->sessionRefNum = theMessage.sessionRefNum;
}
return(theMessage.result);
}
// ---------------------------------------------------------------------------
// ¥ UAM_LoginMSUAM()
// ---------------------------------------------------------------------------
// Log into an AFP server using the .AFPTranslator (through the UAM).
OSStatus UAM_LoginMSUAM(UAMArgs *inUAMArgs)
{
OSStatus theError = noErr;
Ptr theCmdPtr;
UInt32 theCmdSize;
Str32 theWSName;
Str64 theUserName;
char theDomainName[DNLEN+1];
UAMMessage theMessage;
Assert_(inUAMArgs != NULL);
Assert_(PSTR_LENGTH(gAFPVersion) != 0);
//
//11.10.99: Clear out the command buffer just to be safe.
//
ZeroMemory(gCmdBuffer, sizeof(gCmdBuffer));
theCmdPtr = (Ptr)gCmdBuffer;
UAM_PStrCopy(inUAMArgs->Opt.auth.userName, theUserName);
//
//09.28.00: If the user name is blank, then we create a random user name
//so as to attempt a login as "guest".
//
if (PSTR_LENGTH(theUserName) == 0)
{
UAM_PStrCopy("\pGst&^^", theUserName);
}
if (inUAMArgs->callbacks != NULL)
{
UAM_GetWorkStationName(theWSName);
//
//This can be 0, but shouldn't be, catch only for debugging.
//
Assert_(PSTR_LENGTH(theWSName) != 0);
//
//Build the AFP command structure for a login.
//
*theCmdPtr = kFPLogin;
theCmdPtr += sizeof(Byte);
//
//Stuff the AFP command block with our info.
//
StringPush_(gAFPVersion, (StringPtr)theCmdPtr);
//
//Check UAM version that the server supports, use the latest...
//
if (gSupportedUAMs & kMSUAM_V3_Supported)
{
DbgPrint_((DBGBUFF, "Using MS3.0 Encryption"));
StringPush_(PSTR_EncryptedLogin3_0, (StringPtr)theCmdPtr);
}
else if (gSupportedUAMs & kMSUAM_V2_Supported)
{
DbgPrint_((DBGBUFF, "Using MS2.0 Encryption"));
StringPush_(PSTR_EncryptedLogin2_0, (StringPtr)theCmdPtr);
}
else
{
//
//Check and see if the user wants to allow this weak authentication
//to take place.
//
if (gUAMPreferences.flags & UAM_PREFS_REQUIRE_STRONG_ENCRYPTION)
{
//
//Nope, the auth strength is too weak for the user to
//stomach. Exit gracefully.
//
UAM_StandardAlert(
uamErr_AuthenticationMessage,
uamErr_AuthTooWeak,
NULL
);
//
//Since we're returned right from here, we need to
//zero out the password the user entered.
//
RtlSecureZeroMemory(
inUAMArgs->Opt.pwDlg.password,
PSTR_LENGTH(inUAMArgs->Opt.pwDlg.password)+1
);
return(userCanceledErr);
}
DbgPrint_((DBGBUFF, "Using MS1.0 Encryption"));
StringPush_(PSTR_EncryptedLogin1_0, (StringPtr)theCmdPtr);
}
StringPush_(theUserName, (StringPtr)theCmdPtr);
//
//11.23.99 MJC - Only copy the workstation name if
//there is one. If there is no workstation name for the Mac, then
//we pad the end of the buffer with 2 bytes of 0's for NT4 SP6.
//
if (theWSName[0] > 0)
{
StringPush_(theWSName, (StringPtr)theCmdPtr);
}
else if ( ((gSupportedUAMs & kMSUAM_V2_Supported) == 0) &&
((gSupportedUAMs & kMSUAM_V3_Supported) == 0) )
{
//
//If the encryption version is 1.0, then we're talking to an NT 4.0
//or 3.51 file server.
//
//10.17.01
//NOTE: We'll never get here now since UAM_GetWorkStationName()
//always returns a name.
//
*theCmdPtr++ = 0x00;
*theCmdPtr++ = 0x00;
}
//
//For MS3.0 authentication, we need to do a little extra work by
//supplying the server name, domain name, user name and workstation
//name in C-style strings to the continue function.
//
if (gSupportedUAMs & (kMSUAM_V2_Supported | kMSUAM_V3_Supported))
{
Str32 theServerName;
//
//Copy the pascal-style server name so we can convert it to a
//C-style string (null terminated).
//
UAM_PStrCopy(gServerName, theServerName);
//
//Convert all p-strings to c-strings.
//
_p2cstr(theServerName);
_p2cstr(theUserName);
_p2cstr(theWSName);
//
//See if the user name contains a domain name. If not, then pass
//0 as the domain name.
//
memset(theDomainName, 0, sizeof(theDomainName));
if (UAM_ExtractDomainName((char*)theUserName, theDomainName, sizeof(theDomainName)) == TRUE)
{
//
//Since a domain name was included, we need to weed out the
//user name.
//
UAM_ExtractUserName((char*)theUserName, (char*)theUserName, sizeof(theUserName));
}
}
//
//The command block must end on an even boundary!
//
if ((theCmdPtr - (Ptr)gCmdBuffer) % 2)
*theCmdPtr++ = 0x00;
//
//We need to get the command buffer size so we can pass it along.
//
theCmdSize = theCmdPtr - ((Ptr)gCmdBuffer);
//
//We can't be bigger than the max AFP command allowed.
//
Assert_(theCmdSize <= kMaxAFPCommand);
//
//Zero out the parameter block.
//
RtlSecureZeroMemory(&theMessage, sizeof(UAMMessage));
theError = UAM_OpenSession( inUAMArgs,
&theMessage,
gCmdBuffer,
theCmdSize,
gReplyBuffer,
sizeof(gReplyBuffer) );
//
//The error returned should always be afpAuthContinue unless the
//server is rejecting the username.
//
if ( (theError == noErr) ||
(theError == afpAuthContinue) )
{
//
//Save the session reference number to the open AFP
//session we have with the server.
//
inUAMArgs->sessionRefNum = theMessage.sessionRefNum;
BlockMove(gReplyBuffer, &gMSUAMReply, sizeof(MSUAMLoginReplyBlock));
//
//We need to handle the authentication differently for v3.0 versus
//v2.0. The previous 2 versions use LM Hash for security, v3 uses
//NTLMv2.
//
if (gSupportedUAMs & (kMSUAM_V2_Supported | kMSUAM_V3_Supported))
{
theError = UAM_LoginContinueMS20(
inUAMArgs,
(char*)theUserName,
(char*)theDomainName,
(char*)theWSName
);
}
else
{
theError = UAM_LoginContinueMS10(inUAMArgs);
}
}
else
{
//
//The server refused the connection based on the UAM used or
//just the user name.
//
DbgPrint_((DBGBUFF, "OpenSession() returned %d in UAM_LoginMSUAM(), we're bailing...", (int)theError));
//
//The username was rejected most likely. We need to zero out
//the password field for security.
//
RtlSecureZeroMemory(
inUAMArgs->Opt.pwDlg.password,
PSTR_LENGTH(inUAMArgs->Opt.pwDlg.password)+1
);
}
}
return(theError);
}
// ---------------------------------------------------------------------------
// ¥ UAM_PascalStringToCString()
// ---------------------------------------------------------------------------
// Convert a pascal password into a c-style string for LMv2 authentication.
//
void UAM_PascalStringToCString(
const Str255 inPascal,
BOOL inUpcase,
char* outCStyle,
Size inCBufferSize
)
{
Str255 thePascal; //Temporary storage
//
//Make a copy so we don't disrupt the original.
//
UAM_PStrCopy(inPascal, thePascal, sizeof(thePascal));
if (inUpcase) {
UpperString(thePascal, true);
}
//
//02.15.02 MJC: Replace strcpy() with strsafe routine.
//
StringCbCopy(outCStyle, inCBufferSize, _p2cstr(thePascal));
}
// ---------------------------------------------------------------------------
// ¥ UAM_LoginContinueMS20()
// ---------------------------------------------------------------------------
// Continue the login when authenticating using MS2.0 or MS3.0.
//
OSStatus UAM_LoginContinueMS20(
UAMArgs* inUAMArgs,
char* inUserName,
char* inDomainName,
char* inWorkstationName
)
{
char thePassword[UAM_MAX_LMv2_PASSWORD+1];
PUAMLogin2 theCmdPtr;
OSStatus theError = memFullErr;
UInt32 theTimeTillExpiration;
DbgPrint_((DBGBUFF, "Enter UAM_LoginContinueMS20()"));
//
//Make sure the UI is doing it's job and limiting the password to
//only UAM_MAX_LMv2_PASSWORD characters long.
//
if (PSTR_LENGTH(inUAMArgs->Opt.auth.password) > UAM_MAX_LMv2_PASSWORD)
{
Assert_(0);
return(afpParmErr);
}
//
//Convert the pascal password into a c-style string we can use.
//
//01.16.02: Pass maximum string buffer size to conversion routine.
//
UAM_PascalStringToCString(
inUAMArgs->Opt.auth.password,
false,
thePassword,
sizeof(thePassword)
);
//
//Clear out the command buffer just to be safe.
//
ZeroMemory(gCmdBuffer, sizeof(gCmdBuffer));
//
//Build the AFP command block that we'll send to the server.
//
theCmdPtr = (PUAMLogin2)gCmdBuffer;
theCmdPtr->command = kFPLoginCont;
theCmdPtr->pad = '\0';
HRESULT hResult;
ULONG theNegotiateFlags;
ULONG theChallengeMessageLength = 0;
CHALLENGE_MESSAGE* theChallengeMessage = NULL;
ULONG theAuthenticateMessageLength = 0;
AUTHENTICATE_MESSAGE* theAuthenticateMessage = NULL;
USER_SESSION_KEY theUserSessionKey;
//
//Build a "fake" challenge message that we can pass to the NTLMv2 routines.
//We do this since the server does not send us a real NTLM challenge message.
//
hResult = MacSspGenerateChallengeMessage(
gMSUAMReply.serverChallenge,
&theChallengeMessageLength,
&theChallengeMessage
);
if (SUCCEEDED(hResult))
{
theNegotiateFlags = NTLMSSP_NEGOTIATE_UNICODE |
NTLMSSP_NEGOTIATE_ALWAYS_SIGN |
NTLMSSP_NEGOTIATE_NTLM2 |
NTLMSSP_NEGOTIATE_128 |
NTLMSSP_TARGET_TYPE_SERVER;
hResult = MacSspHandleNtlmv2ChallengeMessage(
inUserName,
inDomainName,
inWorkstationName,
thePassword,
theChallengeMessageLength,
theChallengeMessage,
&theNegotiateFlags,
&theAuthenticateMessageLength,
&theAuthenticateMessage,
&theUserSessionKey
);
if (SUCCEEDED(hResult))
{
Assert_(theAuthenticateMessage != NULL);
//
//We have to swap the authentication bytes here as
//MacSspHandleNtlmv2ChallengeMessage() puts the message in
//Windows byte order (littl endian).
//
SspSwapAuthenticateMessageBytes(theAuthenticateMessage);
memcpy(
theCmdPtr->UAMInfo,
((char*)theAuthenticateMessage) + theAuthenticateMessage->LmChallengeResponse.Buffer,
theAuthenticateMessage->LmChallengeResponse.Length
);
theError = UAM_SendAFPRequestToServer(
inUAMArgs,
gCmdBuffer,
sizeof(UAMLogin2),
(UInt8*)&theTimeTillExpiration,
sizeof(UInt32)
);
//
//The reply buffer contains the password expiration time.
//
if (theError == noErr)
{
theTimeTillExpiration = swaplong(theTimeTillExpiration);
gExpirationTime = theTimeTillExpiration;
}
}
}
//
//The challenge message and authenticate message pointers are allocated
//in MacSspHandleNtlmv2ChallengeMessage() and we need to dispose of them.
//
if (theChallengeMessage)
{
delete theChallengeMessage;
}
if (theAuthenticateMessage)
{
delete theAuthenticateMessage;
}
return(theError);
}
// ---------------------------------------------------------------------------
// ¥ UAM_SendAFPRequestToServer()
// ---------------------------------------------------------------------------
// Send the challenge response back to the server.
//
OSStatus UAM_SendAFPRequestToServer(
UAMArgs* inUAMArgs,
unsigned char* inChallengeResponseBuffer,
SInt16 inChallengeResponseBufferSize,
UInt8* ioReplyBuffer,
SInt16 inReplyBufferSize
)
{
UAMMessage theMessage;
OSStatus theError;
//
//We need to zero out the parameter block.
//
ZeroMemory(&theMessage, sizeof(UAMMessage));
theMessage.sessionRefNum = inUAMArgs->sessionRefNum;
theMessage.commandCode = kSendRequest;
theMessage.cmdBuffer = inChallengeResponseBuffer;
theMessage.cmdBufferSize = inChallengeResponseBufferSize;
theMessage.replyBuffer = ioReplyBuffer;
theMessage.replyBufferSize = inReplyBufferSize;
theMessage.completion = NULL;
theMessage.contextPtr = NULL;
theError = UAM_CALLBACK_ONEPARAM(
inUAMArgs->callbacks->SendRequestUPP,
kSendRequestProcInfo,
&theMessage);
//
//The actual error code may lerk in either place
//
theError = (theError == noErr) ? theMessage.result : theError;
//
//For debugging we do this so we know where the error came from.
//
DbgPrint_((DBGBUFF, "SendRequest() returned %d in UAM_SendAFPRequestToServer()", (int)theError));
return(theError);
}
// ---------------------------------------------------------------------------
// ¥ UAM_LoginContinueMS10()
// ---------------------------------------------------------------------------
// Continue the login for v1 authentication.
//
// The following command block is built.
//
// |---------------------|
// | AFP Command | <- One Byte
// |---------------------|
// | Filler |
// |---------------------|
// | Encrypted Password |
// / /
// |---------------------|
OSStatus UAM_LoginContinueMS10(UAMArgs *inUAMArgs)
{
char thePassT[UAM_CLRTXTPWDLEN+1];
OSStatus theError;
UInt32 theTimeTillExpiration;
PUAMLogin theCmdPtr;
DbgPrint_((DBGBUFF, "Enter UAM_LoginContinueMS10()"));
//
//04.27.01: This should never happen as the UI should not allow any password
//to be over our limit. But, we check to make sure.
//
if (PSTR_LENGTH(inUAMArgs->Opt.auth.password) > UAM_CLRTXTPWDLEN)
{
//
//If we were to proceed from here, we would crash as our
//password buffers are not big enough.
//
Assert_(0);
return(afpParmErr);
}
//
//Copy the password as we're going to munge it up and we don't want
//to munge the original, case sensitive, copy.
//
UAM_PascalStringToCString(
inUAMArgs->Opt.auth.password,
true,
thePassT,
sizeof(thePassT)
);
//
//Map extended characters to the correct values for NT.
//
if (!UAM_MapCharactersIntoHostSet(thePassT, gMSUAMReply.serverExtCharMapTable))
{
DbgPrint_((DBGBUFF, "UAM_MapCharactersIntoHostSet() failed"));
return(afpUserNotAuth);
}
//
//11.10.99: Clear out the command buffer just to be safe.
//
ZeroMemory(gCmdBuffer, sizeof(gCmdBuffer));
theCmdPtr = (PUAMLogin)gCmdBuffer;
theCmdPtr->command = kFPLoginCont;
theCmdPtr->pad = '\0';
//
//Get the encrypted LmOwf password using the encrypting routines.
//
if (!UAM_GetEncryptedLmOwfPassword(
thePassT,
gMSUAMReply.serverChallenge,
theCmdPtr->UAMInfo))
{
Assert_(0);
return(afpMiscErr);
}
//
//Be a good security citizen and zero out the password buffer
//that we don't need anymore.
//
RtlSecureZeroMemory(thePassT, sizeof(thePassT));
//
//The encrypted OWF should never be null.
//
Assert_(strlen(theCmdPtr->UAMInfo) > 0);
theError = UAM_SendAFPRequestToServer(
inUAMArgs,
gCmdBuffer,
sizeof(UAMLogin),
(UInt8*)&theTimeTillExpiration,
sizeof(UInt32)
);
//
//The reply buffer contains the password expiration time.
//
if (theError == noErr)
{
theTimeTillExpiration = swaplong(theTimeTillExpiration);
gExpirationTime = theTimeTillExpiration;
}
return(theError);
}
// ---------------------------------------------------------------------------
// ¥ UAM_ChangePasswordForMS10()
// ---------------------------------------------------------------------------
// Change the user's password on the server.
//
// -> inUAMArgs The UAM arguments.
// -> inNewPwd The new password requested by the user.
//
// Returns: An error code or noErr.
//
// |---------------------|
// | AFP Command | <- One Byte
// |---------------------|
// | New pwd len |
// |---------------------|
// | UAM String |
// / /
// |---------------------|
// | User Name |
// / /
// |---------------------|
// | Old Pwd (Encrypted) |
// / /
// |---------------------|
// | New Pwd (Encrypted) |
// / /
// |---------------------|
OSStatus UAM_ChangePasswordForMS10(UAMArgs *inUAMArgs, StringPtr inNewPwd)
{
OSStatus theError;
Ptr theCmdPtr;
short theCmdSize;
char oldPassword[UAM_CLRTXTPWDLEN+1];
char newPassword[UAM_CLRTXTPWDLEN+1];
Assert_(inNewPwd != NULL);
Assert_(inUAMArgs != NULL);
//
//Check to make sure we don't go over our maximum
//buffer size. The UI should prevent this, but...
//
if ((PSTR_LENGTH(inUAMArgs->Opt.auth.password) > UAM_CLRTXTPWDLEN) ||
(PSTR_LENGTH(inNewPwd) > UAM_CLRTXTPWDLEN) )
{
//
//Our buffers aren't big enough, a password that is too big was passed.
//
Assert_(0);
return(afpParmErr);
}
//
//Convert everything to C strings and uppercase them as well.
//
UAM_PascalStringToCString(
inUAMArgs->Opt.auth.password,
true,
oldPassword,
sizeof(oldPassword)
);
UAM_PascalStringToCString(
inNewPwd,
true,
newPassword,
sizeof(newPassword)
);
//
//Log into the server. Note that if the password is expired, the server
//will let us make one AFP call, that being FPChangePassword over
//the opened session.
//
theError = UAM_LoginMSUAM(inUAMArgs);
if ((theError != noErr) &&
(theError != afpNTPasswordExpired) &&
(theError != afpPwdExpiredErr) )
{
//
//If we couldn't login or the password wasn't expired, we
//cannot continue.
//
return(theError);
}
ZeroMemory(gCmdBuffer, sizeof(gCmdBuffer));
theCmdPtr = (Ptr)gCmdBuffer;
*theCmdPtr++ = afpChangePwd;
*theCmdPtr++ = strlen(newPassword);
StringPush_(PSTR_EncryptedLogin1_0, theCmdPtr);
if ((theCmdPtr - ((Ptr)gCmdBuffer)) % 2)
{
*theCmdPtr++ = 0x00;
}
StringPush_(inUAMArgs->Opt.auth.userName, theCmdPtr);
//
//Make sure the buffer ends on an even boundary before the next block.
//
if ((theCmdPtr - ((Ptr)gCmdBuffer)) % 2)
{
*theCmdPtr++ = 0x00;
}
theCmdSize = theCmdPtr - ((Ptr)gCmdBuffer);
theError = afpNTPasswordProcessFailure;
if ( (UAM_MapCharactersIntoHostSet(oldPassword, gMSUAMReply.serverExtCharMapTable)) &&
(UAM_MapCharactersIntoHostSet(newPassword, gMSUAMReply.serverExtCharMapTable))
)
{
//
//Call the magic NTLM routine to encrypt the passwords into one
//single buffer.
//
if (UAM_GetDoubleEncryptedLmOwfPasswords(oldPassword, newPassword, theCmdPtr))
{
//
//Make a couple last calculations so we can determine the size of the CB.
//
theCmdSize += (kOneWayEncryptedArgSize * 2);
theError = UAM_SendAFPRequestToServer(
inUAMArgs,
gCmdBuffer,
theCmdSize,
gReplyBuffer,
kMaxAFPCommand
);
}
}
DBGPrintIfOSErr_((int)theError);
//
//We're done with the session, so close it out and return any error codes.
//
UAM_CloseSession(inUAMArgs);
return(theError);
}
// ---------------------------------------------------------------------------
// ¥ UAM_ChangePasswordForMS20()
// ---------------------------------------------------------------------------
// Change the user's password on the server using Microsoft V2.0. This new
// function was required so we can pass the actual password encrypted
// over the wire to the server. The server needs the password so it can
// update the stored clear text password on the DS (Domain Controller).
//
// -> inUAMArgs The UAM arguments.
// -> inNewPwd The new password requested by the user.
//
// Returns: An error code or noErr.
//
// |--------------------------------|
// | AFP Command | <- One Byte
// |--------------------------------|
// | New pwd len |
// |--------------------------------|
// | UAM String | <- Always PSTR_EncryptedLogin2_0
// / /
// |--------------------------------|
// | User Name | <- Variable length
// / /
// |--------------------------------|
// | PENCRYPTED_NT_OWF_PASSWORD | <- Variable length
// / /
// |--------------------------------|
// | PSAMPR_ENCRYPTED_USER_PASSWORD | <- Variable length
// / /
// |--------------------------------|
//
OSStatus UAM_ChangePasswordForMS20(UAMArgs *inUAMArgs, const StringPtr inNewPwd)
{
OSStatus theError;
Ptr theCmdPtr;
short theCmdSize;
char oldPassword[UAM_CLRTXTPWDLEN+1];
char newPassword[UAM_CLRTXTPWDLEN+1];
char newStdPassword[UAM_CLRTXTPWDLEN+1];
SAMPR_ENCRYPTED_USER_PASSWORD theNewEncryptedWithLm;
ENCRYPTED_NT_OWF_PASSWORD theOldLmOwfEncryptedWithNewLm;
Assert_(inNewPwd != NULL);
Assert_(inUAMArgs != NULL);
//
//Check to make sure we don't go over our maximum
//buffer size. The UI should prevent this, but...
//
if ((PSTR_LENGTH(inUAMArgs->Opt.auth.password) > UAM_CLRTXTPWDLEN) ||
(PSTR_LENGTH(inNewPwd) > UAM_CLRTXTPWDLEN) )
{
//
//Our buffers aren't big enough, a password that is too big was passed.
//
Assert_(0);
return(afpParmErr);
}
//
//Convert everything to C strings and uppercase them as well. DO NOT
//uppercase newStdPassword as this is passed as is over the wire.
//
UAM_PascalStringToCString(
inUAMArgs->Opt.auth.password,
true,
oldPassword,
sizeof(oldPassword)
);
UAM_PascalStringToCString(
inNewPwd,
true,
newPassword,
sizeof(newPassword)
);
UAM_PascalStringToCString(
inNewPwd,
false,
newStdPassword,
sizeof(newStdPassword)
);
//
//Log into the server. Note that if the password is expired, the server
//will let us make one AFP call, that being FPChangePassword over
//the opened session.
//
theError = UAM_LoginMSUAM(inUAMArgs);
if ((theError != noErr) &&
(theError != afpNTPasswordExpired) &&
(theError != afpPwdExpiredErr) )
{
//
//If we couldn't login or the password wasn't expired, we
//cannot continue.
//
return(theError);
}
//
//Start with a clean slate in our command buffer.
//
ZeroMemory(gCmdBuffer, sizeof(gCmdBuffer));
theCmdPtr = (Ptr)gCmdBuffer;
*theCmdPtr++ = afpChangePwd;
*theCmdPtr++ = strlen(newPassword);
StringPush_(PSTR_EncryptedLogin2_0, theCmdPtr);
StringPush_(inUAMArgs->Opt.auth.userName, theCmdPtr);
//
//Make sure the ptr is aligned on an even boundary at this point
//
if ((theCmdPtr - (Ptr)gCmdBuffer) % 2)
{
DbgPrint_((DBGBUFF, "Aligning for even boundary (size = %d)", (theCmdPtr - (Ptr)gCmdBuffer)));
*theCmdPtr++ = 0x00;
}
//
//We doctor up the error code here since the only error that
//SampEncryptLmPasswords() returns is INVALID_FUCTION. We assume
//failure here.
//
theError = afpNTPasswordProcessFailure;
//
//Map the extended characters from Mac to Windows. NOTE that we do it for all
//3 buffers because newPassword and newStdPassword are really different!
//
if ( (UAM_MapCharactersIntoHostSet(oldPassword, gMSUAMReply.serverExtCharMapTable)) &&
(UAM_MapCharactersIntoHostSet(newPassword, gMSUAMReply.serverExtCharMapTable)) &&
(UAM_MapCharactersIntoHostSet(newStdPassword, gMSUAMReply.serverExtCharMapTable))
)
{
RtlSecureZeroMemory(&theNewEncryptedWithLm, sizeof(SAMPR_ENCRYPTED_USER_PASSWORD));
RtlSecureZeroMemory(&theOldLmOwfEncryptedWithNewLm, sizeof(ENCRYPTED_NT_OWF_PASSWORD));
DbgPrint_((DBGBUFF, "Old password = %s", oldPassword));
DbgPrint_((DBGBUFF, "New password = %s", newPassword));
DbgPrint_((DBGBUFF, "New case sensitive = %s", newStdPassword));
//
//Call the magic function with will encrypt the password(s) for us.
//
theError = MacSspSampEncryptLmPasswords(
oldPassword,
newPassword,
newStdPassword,
&theNewEncryptedWithLm,
&theOldLmOwfEncryptedWithNewLm
);
if (SUCCEEDED(theError))
{
//
// Copy the ENCRYPTED_NT_OWF_PASSWORD into the command buffer.
//
DataPush_(&theOldLmOwfEncryptedWithNewLm, sizeof(ENCRYPTED_NT_OWF_PASSWORD), theCmdPtr);
//
//Copy the SAMPR_ENCRYPTED_USER_PASSWORD into the command buffer.
//
DataPush_(&theNewEncryptedWithLm, sizeof(SAMPR_ENCRYPTED_USER_PASSWORD), theCmdPtr);
//
//Make a last minute calculation so we can determine the size of the CB.
//
theCmdSize = theCmdPtr - (Ptr)(&gCmdBuffer);
Assert_(theCmdSize <= kMaxAFPCommand);
DbgPrint_((DBGBUFF, "Change password v2 cmd size = %d", theCmdSize));
theError = UAM_SendAFPRequestToServer(
inUAMArgs,
gCmdBuffer,
theCmdSize,
gReplyBuffer,
kMaxAFPCommand
);
}
}
DBGPrintIfOSErr_((int)theError);
//
//We're done with the session, so close it out and return any error codes.
//
UAM_CloseSession(inUAMArgs);
return(theError);
}
// ---------------------------------------------------------------------------
// ¥ UAM_ChangePasswordForMS30()
// ---------------------------------------------------------------------------
// Change the user's password on the server using Microsoft V3.0.
//
OSStatus UAM_ChangePasswordForMS30(
UAMArgs* inUAMArgs,
const StringPtr inNewPwd
)
{
char theNewPassword[UAM_MAX_LMv2_PASSWORD+1];
char theOldPassword[UAM_MAX_LMv2_PASSWORD+1];
Ptr theCmdPtr;
SInt16 theCmdSize;
OSStatus theError = memFullErr;
SFM_PASSWORD_CHANGE_MESSAGE theChangeMessage;
//
//Log into the server. Note that if the password is expired, the server
//will let us make one AFP call, that being FPChangePassword over
//the opened session.
//
theError = UAM_LoginMSUAM(inUAMArgs);
if ((theError != noErr) &&
(theError != afpNTPasswordExpired) &&
(theError != afpPwdExpiredErr) )
{
//
//If we couldn't login or the password wasn't expired, we
//cannot continue.
//
return(theError);
}
//
//Convert the pascal passwords into c-style strings we can use and
//map extended characters to the correct values for NT.
//
UAM_PascalStringToCString(
inUAMArgs->Opt.auth.password,
false,
theOldPassword,
sizeof(theOldPassword)
);
UAM_PascalStringToCString(
inNewPwd,
false,
theNewPassword,
sizeof(theNewPassword)
);
//
//Start with a clean slate in our command buffer.
//
ZeroMemory(gCmdBuffer, sizeof(gCmdBuffer));
theCmdPtr = (Ptr)gCmdBuffer;
*theCmdPtr++ = afpChangePwd;
*theCmdPtr++ = strlen(theNewPassword);
StringPush_(PSTR_EncryptedLogin3_0, theCmdPtr);
StringPush_(inUAMArgs->Opt.auth.userName, theCmdPtr);
//
//Make sure the ptr is aligned on an even boundary at this point
//
if ((theCmdPtr - (Ptr)gCmdBuffer) % 2) {
*theCmdPtr++ = 0x00;
}
StringCbCopy(
(char*)theChangeMessage.Signature,
sizeof(theChangeMessage.Signature),
SFM_CHANGE_PASSWORD_SIGNATURE
);
theChangeMessage.cbMessage = sizeof(theChangeMessage);
theChangeMessage.cbMessage = swaplong(theChangeMessage.cbMessage);
theChangeMessage.Version = SFM_CHANGE_PASSWORD_VERSION;
theChangeMessage.Version = swaplong(theChangeMessage.Version);
theError = MacSspSamiEncryptCStringPasswords(
theOldPassword,
theNewPassword,
&theChangeMessage.NewPasswordEncryptedWithOldNt,
&theChangeMessage.OldNtOwfPasswordEncryptedWithNewNt
);
if (SUCCEEDED(theError))
{
//
//For Macintosh, since we can only have a maximum password length of
//64 bytes, we only send the second half of the encrypted NT password.
//This allows us to leave extra room in our request packet to handle
//a 64 byte long domain\user name.
//
SFM_PASSWORD_CHANGE_MESSAGE_SHORT theShortMsg;
StringCbCopy(
(char*)theShortMsg.Signature,
sizeof(theShortMsg.Signature),
(char*)theChangeMessage.Signature
);
theShortMsg.cbMessage = sizeof(SFM_PASSWORD_CHANGE_MESSAGE_SHORT);
theShortMsg.cbMessage = swaplong(theShortMsg.cbMessage);
theShortMsg.Version = theChangeMessage.Version;
memcpy(
&theShortMsg.NewPasswordEncryptedWithOldNt,
(((unsigned char*)&theChangeMessage.NewPasswordEncryptedWithOldNt))+
(sizeof(theChangeMessage.NewPasswordEncryptedWithOldNt)/2),
sizeof(theShortMsg.NewPasswordEncryptedWithOldNt)
);
memcpy(
&theShortMsg.OldNtOwfPasswordEncryptedWithNewNt,
&theChangeMessage.OldNtOwfPasswordEncryptedWithNewNt,
sizeof(theShortMsg.OldNtOwfPasswordEncryptedWithNewNt)
);
DataPush_(&theShortMsg, sizeof(theShortMsg), theCmdPtr);
//
//Make a last minute calculation so we can determine the size of the CB.
//
theCmdSize = theCmdPtr - (Ptr)(&gCmdBuffer);
Assert_(theCmdSize <= kMaxAFPCommand);
DbgPrint_((DBGBUFF, "Change password v3 cmd size = %d", theCmdSize));
theError = UAM_SendAFPRequestToServer(
inUAMArgs,
gCmdBuffer,
theCmdSize,
gReplyBuffer,
kMaxAFPCommand
);
}
DBGPrintIfOSErr_((int)theError);
//
//We're done with the session, so close it out and return any error codes.
//
UAM_CloseSession(inUAMArgs);
return(theError);
}
|