sources/PGPUAMclient.c

/*
    File:           PGPUAMclient.c
 
    Description:    PGP Appleshare User Authentication Module
 
    Written by: Vinnie Moscaritolo
 
    Copyright:  © 1998 by Apple Computer, Inc., all rights reserved.
 
    Change History (most recent first):
 
    You may incorporate this sample code into your applications without
    restriction, though the sample code has been provided "AS IS" and the
    responsibility for its operation is 100% yours.  However, what you are
    not permitted to do is to redistribute the source as "DSC Sample Code"
    after having made changes. If you're going to re-distribute the source,
    we require that you make it clear in the source that the code was
    descended from Apple Sample Code, but that you've made changes.
*/
 
//------------------------------------------------------------------------------------
#pragma mark Includes
//------------------------------------------------------------------------------------
#include <Errors.h>
#include <String.h>
#include <A4Stuff.h>
#include <Resources.h>
#include <MixedMode.h>
#include <Appearance.h>
#include <CodeFragments.h>
#include <Gestalt.h>
#include <PLStringFuncs.h>
 
#include "ClientUAM.h" 
#include "AFPPackets.h"
 
#include <stdio.h>
 
#include "TPGPkey.h"
#include "TPGPException.h"
#include "TMacException.h"
#include "TASIPPGPkey.h"
#include "ASIPChallenge.h"
#include "PGPUAMdefines.h"
#include "PGPUAMclient.h"
#include "PGPUAMclientLoginDialog.h"
#include "PGPUAMclientProtocol.h"
#include "PGPUAMmsgFormat.h"
#include "PGPUAMclient.h"
#include "TPGPUAMPrefs.h"
#include "pgpUserInterface.h"
 
 
 
// ---------------------------------------------------------------------------
#pragma mark Exported Symbols
// ---------------------------------------------------------------------------
 
#ifdef __cplusplus
extern "C" {
#endif
#pragma export on
    extern  const long __procinfo = kUAMCallProcInfo;
            OSErr       __pgpuam_initialize(CFragInitBlockPtr ibp);
            void        __pgpuam_terminate(void);
    pascal  OSStatus    __pgpuam_client (UAMArgs *theArgs);
 
    pascal OSErr __initialize(const CFragInitBlock *theInitBlock);
    pascal OSErr __terminate(void);
    #pragma export off
 
 
#ifdef __cplusplus
}
#endif
 
 
// ---------------------------------------------------------------------------
#pragma mark Globals
// ---------------------------------------------------------------------------
    AFPSrvrInfo *   gAFPServerInfo  = nil;
    AFPClientInfo * gClientInfo     = nil;
    UInt8 *         gAFPSrvrSig     = nil;
 
// ---------------------------------------------------------------------------
#pragma mark Local Prototypes
// ---------------------------------------------------------------------------
 
static  OSStatus    PGPUAMOpen  (UAMArgs *theArgs);
static  OSStatus    PGPUAMClose (UAMArgs *theArgs);
static  OSStatus    PGPUAMLogin (UAMArgs *theArgs);
 
 
static  OSStatus    InitiateConnection (StringPtr userName, 
                                        StringPtr serverName,
                                        short *sessionRefNum, 
                                        TPGPUAMPrefs *userPrefs, 
                                        TASIPPGPkey *serverKey, 
                                        IdleConnectionProcPtr idleProc, 
                                        void* context );
                                        
static  void        TerminateConnection (void* context);
 
static StringPtr    FigureAFPVersion(   AFPSrvrInfo *info,ClientUAMCallbackRec *callbacks);
static Boolean      FindStringInBuf(    StringPtr string, Ptr buf, UInt32 bufSize);
 
static void         PostError(short errorStrId, short explanStrID);
 
// test code..
 
// ---------------------------------------------------------------------------
OSErr __pgpuam_initialize(CFragInitBlockPtr ibp)
// ---------------------------------------------------------------------------
//
//  PGPUAM Library Initialization
//
 
{
    long    gestaltResult;
    OSErr   err = -1;
    
    /*
    ** Check for a 68020 or later processor and bail immediately if
    ** none is available.
    */
//DebugStr("\p__pgpuam_initialize");
 
    if( Gestalt( gestaltProcessorType, &gestaltResult ) == noErr &&
        gestaltResult >= gestalt68020 )
        {
            err = __initialize( ibp );
        }
 
    return err;
}
 
 
// ---------------------------------------------------------------------------
void __pgpuam_terminate(void)
// ---------------------------------------------------------------------------
//
//  PGPUAM Library Termination
//
 
{
    __terminate();
}
 
 
#pragma mark -
// ---------------------------------------------------------------------------
pascal OSStatus __pgpuam_client(UAMArgs *theArgs)
// ---------------------------------------------------------------------------
{
    OSStatus    error;
    char        errorBuf[256];
    char        explanationBuf[256];
//  ostrstream err( errorBuf,256);
//  ostrstream explain( explanationBuf,256);
 
    EnterCodeResource();
    try
    {
        switch(theArgs->command)
        {
            case    kUAMOpen:
    //          DebugStr("\pUAMCall - kUAMOpen");
                error = PGPUAMOpen(theArgs);
                break;
                                    
            case    kUAMPWDlog:
    //          DebugStr("\pUAMCall - kUAMPWDlog");
                error = noErr;
                break;
 
            case    kUAMLogin:
    //          DebugStr("\pUAMCall - kUAMLogin");
                error = PGPUAMLogin(theArgs);
                break;
            
            case    kUAMVSDlog:
    //          DebugStr("\pUAMCall - kUAMVSDlog");
                error = noErr;
                break;
 
            case    kUAMChgPassDlg:
                DebugStr("\pUAMCall - kUAMChgPassDlg");
                error = kNotForUs;
                break;
 
            case    kUAMChgPass:
                DebugStr("\pUAMCall - kUAMChgPass");
                error = kNotForUs;
                break;
 
            case    kUAMGetInfoSize:
                DebugStr("\pUAMCall - kUAMGetInfoSize");
    //          uamInfoSize = n;
                error = kNotForUs;
                break;
 
            case    kUAMGetInfo:
                DebugStr("\pUAMCall - kUAMGetInfo");
    //          uamInfo < == 
                error = kNotForUs;
                break;
 
            case    kUAMClose:
    //          DebugStr("\pUAMCall - kUAMClose");
                error = PGPUAMClose(theArgs);
                break;
 
            default:
                DebugStr("\psome other UAMCall");
                error = kNotForUs;
                break;
        }
    }
    catch (TMacException &ex)
    {
        SInt16          itemHit;
    
        error = theArgs->result = ex.GetExceptionErr(); 
        if(error == noErr) 
                error = userCanceledErr;
 
        errorBuf[0] = sprintf(&errorBuf[1], "%s\rPGPUAM Error: %d\rFile: %s, Line: %d",  
                             ex.GetExceptionMessage(),  ex.GetExceptionErr(), ex.GetExceptionFile(), ex.GetExceptionLine());
                             
        StandardAlert(kAlertStopAlert,"\pThe PGP Challenge/Response UAM could not be used.", 
                                        (UInt8 *) errorBuf, nil , &itemHit);
    }
 
// handle PGP errors
    catch (TPGPException &ex)
    {
        SInt16          itemHit;
        PGPGetErrorString ( ex.GetExceptionErr(), sizeof(explanationBuf), explanationBuf);
        
        errorBuf[0] = sprintf(&errorBuf[1], "%s\r\rPGPUAM Error: %d\rFile: %s, Line: %d",  
                            ex.GetExceptionMessage(),  ex.GetExceptionErr(),  ex.GetExceptionFile(),  ex.GetExceptionLine());
    
        c2pstr(explanationBuf);
        StandardAlert(kAlertStopAlert,(UInt8 *)errorBuf, (UInt8 *)explanationBuf, nil , &itemHit);
        error = theArgs->result = userCanceledErr; 
    }
 
    ExitCodeResource();
    return error;
} 
 
// ---------------------------------------------------------------------------
static OSStatus PGPUAMOpen(UAMArgs *theArgs)
// ---------------------------------------------------------------------------
 {
    OSStatus    ErrNo ; 
    long        response = 0;
 
// Get the AppleShare Client info
    ErrNo = CallUniversalProc( theArgs->callbacks->GetClientInfoUPP, kGetClientInfoProcInfo,  kAFPClientInfo, &gClientInfo);
    if(ErrNo != noErr) return ErrNo;
    
// check agaisnt client versions
    ThrowMsgIfNot ( (gClientInfo->fVersion > 8), 
        "This UAM requires AppleShare Client 3.8.1 or later");
    
// Save the server info pointer.
    gAFPServerInfo  = theArgs->Opt.open.srvrInfo;
    
// calculate server signature block.
        UInt8           *signatureOffset;
        signatureOffset =  (&gAFPServerInfo->fSrvrName[0] + gAFPServerInfo->fSrvrName[0] + 1);
        if ( (UInt32)signatureOffset & 0x01) signatureOffset++;
        gAFPSrvrSig = ((UInt8*) gAFPServerInfo)  + * ((short*)signatureOffset);
 
// Is  Appearance Mgr available ?
    ThrowMsgIfNot ( ((Gestalt(gestaltAppearanceAttr, &response) == noErr) 
                    && ((long)RegisterAppearanceClient != kUnresolvedCFragSymbolAddress)) ,
         "Appearance Mgr is required");     
 
 // collection manager too
    ThrowMsgIfNot ( ((Gestalt(gestaltCollectionMgrVersion, &response) == noErr) 
                    && ((long)NewCollection != kUnresolvedCFragSymbolAddress)) ,
         "Collection Mgr is required");     
 
// Is  PGPsdk available ?
    ThrowMsgIfNot ((long)PGPNewContext != kUnresolvedCFragSymbolAddress,
         "PGPsdk is required");     
 
// Is  PGPsdkUIlib available ?
    ThrowMsgIfNot ((long)PGPSigningPassphraseDialog != kUnresolvedCFragSymbolAddress ,
         "PGPsdkUIlib is required");    
 
// Create a new PGP context
    TPGPkey::Initialize();
 
// establish link to PassphraseCache
//???
 
    theArgs->result = ((1 << kUsePWDlog) | (1 << kUseUAMInfo)) ;
    return noErr;
}
 
// ---------------------------------------------------------------------------
static OSStatus PGPUAMLogin(UAMArgs *theArgs)
// ---------------------------------------------------------------------------
{
    TPGPUAMPrefs*   userPrefs   = nil;
    Str63           userName;
 
// Get user prefs   
    userPrefs = new  TPGPUAMPrefs();
    userPrefs->Initialize();
 
// open the keyring 
    TPGPkey::OpenKeyDefaultRing();
 
// if no username was specified.. (Alias resolution)
// Initiate the connection w/o UI
    if(theArgs->Opt.auth.userName[0] != '\0')
    {
        TASIPPGPkey     theServerKey;
        
    // copy the user name from the authblock
        PLstrcpy( userName, theArgs->Opt.auth.userName);
    
    
    // pickup the server key
        theServerKey.Initialize(gAFPSrvrSig);
        
    // initiate connection directly
        theArgs->result = InitiateConnection(userName, gAFPServerInfo->fSrvrName, &theArgs->sessionRefNum, userPrefs, &theServerKey, nil, theArgs);
    
     } 
    else 
    {
    // copy the default user name.
        PLstrcpy( userName, gClientInfo->fDefaultUserName);
 
    // initiate connection with User Interface
        theArgs->result = DoLoginDialog( userName, 
                                         gAFPServerInfo->fSrvrName,
                                         &theArgs->sessionRefNum,
                                         userPrefs,
                                         InitiateConnection,
                                         TerminateConnection,
                                         theArgs->callbacks->EventCallbackUPP,
                                         theArgs);
 
    // put username into the authblock buffer for later Alias resolution
        PLstrcpy(theArgs->Opt.auth.userName, userName);
        
    // not clear if I should post the error or not
    // you might want to take out the next line...
        if(( theArgs->result != noErr) && (theArgs->result != userCanceledErr))
             PostError( kErrOther, kErrOtherExplanation);
    }
 
// close the keyring
    TPGPkey::CloseKeyRing();
 
// update user prefs
    userPrefs->Finalize();
    delete userPrefs;
        
    return (theArgs->result) ;
}
 
// ---------------------------------------------------------------------------
static OSStatus PGPUAMClose(UAMArgs *theArgs)
// ---------------------------------------------------------------------------
 {
 
// remove PGP context       
        TPGPkey::Finalize();
        
    return noErr;
}
 
#pragma mark -
 
 
// ---------------------------------------------------------------------------
static OSStatus InitiateConnection( StringPtr   userName,
                                    StringPtr   serverName, 
                                    short      *sessionRefNum,
                                    TPGPUAMPrefs *userPrefs,
                                    TASIPPGPkey *serverKey,
                        IdleConnectionProcPtr   idleProc,
                                    void*       context)
// ---------------------------------------------------------------------------
{
    UAMArgs         *theArgs = (UAMArgs *) context;
    PUAM_LOGIN_RESP resp;
    StringPtr       serverVersion;
    Str63           challengeString;
    Str63           answerString;
    Str255          promptString;
 
    UInt8           replyBuffer[256];
    UInt32          replyBufferSize;
    Handle          uamName;
    OSStatus        ErrNo ;     
  
    // calculate which AFPversion we will use.
    serverVersion = FigureAFPVersion(gAFPServerInfo,theArgs->callbacks);
    if(!serverVersion){
        // put up an alert & return userCancelled error
        DebugStr("\pno AFP version returned by server");
        return userCanceledErr;
    }
 
    // get the UAMSTring from the resource
    uamName = Get1Resource(kUAMStr,kUAMProtoName);
    ThrowMacErrIfNil(uamName, ResError());
    
    // Calculate Challenge String   
    MakeChallenge(serverKey, challengeString);
    
    // send OpenSession command to server.
    HLock(uamName);
    ErrNo = SndLoginCmd(    theArgs->callbacks,
                            serverVersion, (StringPtr) *uamName,  userName, 
                            challengeString,
                            theArgs->Opt.auth.srvrAddress, 
                            replyBuffer,   sizeof (replyBuffer),  &replyBufferSize,                 
                            sessionRefNum,
                            idleProc);   
    HUnlock(uamName);
    if( ErrNo != kFPAuthContinue )  return ErrNo;
    ErrNo = userCanceledErr;
    
    // parse the reply
    ParseLoginResp(replyBuffer,  &replyBufferSize, &resp);
 
    // check the reply
    if( !  VerifyChallenge(serverKey, challengeString, resp.CounterChallengePString))
    {
 
// warn user that server didnt check out!
        Str255      errorBuf, explainBuf;
        SInt16      itemHit;
        AlertStdAlertParamRec aRec;
        
        aRec.movable    = true;
        aRec.helpButton = false;
        aRec.filterProc = nil;
        aRec.defaultText =  userPrefs->GetAuthenticateServer()? (unsigned char*) -1 : "\pAbort";
        aRec.cancelText  =  userPrefs->GetAuthenticateServer()? nil:"\pContinue";
        aRec.otherText   = nil;
        aRec.defaultButton = kAlertStdAlertOKButton;
        aRec.cancelButton  = kAlertStdAlertCancelButton;
        aRec.position  = kWindowAlertPositionParentWindowScreen;
            
        GetIndString(errorBuf, kErrorStringsID, kErrUnableToAuthErr);
        PLstrcat(errorBuf, "\p\"");
        PLstrcat(errorBuf, serverName);
        PLstrcat(errorBuf, "\p\".");
        GetIndString(explainBuf, kErrorStringsID,  userPrefs->GetAuthenticateServer() ? kErrUnableToAuthExplanation: kErrUnableToAuthContinueExplanation);
        
        StandardAlert(kAlertCautionAlert, errorBuf, explainBuf, &aRec , &itemHit);
        
        if(itemHit == kAlertStdAlertOKButton) return userCanceledErr;
    };
 
    // build custom prompt string
    PLstrcpy(promptString, "\pTo connect to \"");
    PLstrcat(promptString, serverName);
    PLstrcat(promptString, "\p\", please enter the passphrase for the following key.");
    p2cstr(promptString);
    
    ErrNo = ReplyToCounterChallenge(promptString, resp.FingerPrintPString, resp.CounterChallengePString, answerString);
    if(ErrNo == userCanceledErr) return ErrNo;
    
    if(ErrNo != noErr)
    {
        PostError(kErrClientUnableToAuthErr, kErrClientUnableToAuthExplanation);
        return userCanceledErr;
    }
    
    ErrNo = SndLoginContinueCmd(theArgs->callbacks, *sessionRefNum, answerString,
                                    replyBuffer,   sizeof (replyBuffer),  &replyBufferSize, idleProc);  
    
    return ErrNo;
}
 
 
 
// ---------------------------------------------------------------------------
static void PostError(short errorStrId,short explanStrID)
// ---------------------------------------------------------------------------
//
{
        Str255      errorBuf, explainBuf;
        SInt16      itemHit;
 
        GetIndString(errorBuf, kErrorStringsID, errorStrId);
 
        if(explanStrID) 
            GetIndString(explainBuf, kErrorStringsID, explanStrID);
 
        StandardAlert(kAlertStopAlert,errorBuf, (explanStrID)?explainBuf:nil, nil , &itemHit);
}
 
// ---------------------------------------------------------------------------
static void TerminateConnection(void* context)
// ---------------------------------------------------------------------------
{
    UAMArgs *theArgs = (UAMArgs *) context;
     
}
 
#pragma mark -
  
// ---------------------------------------------------------------------------
static StringPtr    FigureAFPVersion(AFPSrvrInfo *info,ClientUAMCallbackRec *callbacks)
// ---------------------------------------------------------------------------
{
    struct  AFPClientInfo *theClientInfo = nil;
    short   index;
    Ptr     versBuf;
    UInt32  versBufsize;
 
#if 0
// Hack
    return "\pAFP TEST VERSION";
// Hack
#endif
    
//DebugStr("\pFigureAFPVersion");
 
    CallUniversalProc( callbacks->GetClientInfoUPP, kGetClientInfoProcInfo,  kAFPClientInfo, &theClientInfo);
 
    if(theClientInfo){              
    // go through the list of AFP versions supported and try to find them
    // in the SrvrInfoBuffer, first match gets it 
        versBuf = (Ptr)((UInt32)info + info->fVerCountOffset+1);
        versBufsize = kMaxAFPCommand - info->fVerCountOffset;   // the largest size
        
        for(index = 0; index < theClientInfo->fNumAFPVersions; index++){
            if(FindStringInBuf(theClientInfo->fAFPVersionStrs[index],versBuf,versBufsize)){
                return theClientInfo->fAFPVersionStrs[index];
            }
        }
    }
    return  nil;
}
 
// ---------------------------------------------------------------------------
static Boolean FindStringInBuf(StringPtr string, Ptr buf, UInt32 bufSize)
// ---------------------------------------------------------------------------
{
    Ptr     end = buf + bufSize;
    Byte    len = string[0] + 1;
    short   index;
    
    while((buf < end) && (*buf++ != string[0])) ;   // scan for the proper length
 
    if(!(buf < end)){
        return false;
    }
    for(index = 1; (index < len) && (buf > end); index++){
        if(*buf++ != string[index])
            return false; 
    }
    if(!(buf < end)){
        return false;
    }
    return true;
}