Hello -
Mac OS 10.3 (Panther) introduces, by default, a new password
authentication mechanism. The standard Unix functions to get at the
crypt-ed password no longer work. Instead, one must go through Apple's
Directory Service API.
I am attaching a patch (mostly derived from Apple's sample code) that
reenables password authentication under Panther.
This code *should* work on previous versions of OS X, but I have only
tested it on Panther.
In order for this patch to work, you must have installed Apple's Developer
Tools, which are distributed with the OS on a separate CD.
Please email me with questions/comments.
Thanks,
Tom Brown
diff -Nrup imap-2002e/src/osdep/unix/Makefile
/usr/local/src/imap-2002e/src/osdep/unix/Makefile
--- imap-2002e/src/osdep/unix/Makefile Mon Jul 14 21:24:24 2003
+++ /usr/local/src/imap-2002e/src/osdep/unix/Makefile Tue Oct 28 20:28:38 2003
@@ -525,9 +525,10 @@ os4: # OSF/1 (Digital UNIX) 4
osx: # Mac OS X
$(BUILD) `$(CAT) SPECIALS` OS=$@ \
- CRXTYPE=nfs \
+ CHECKPW=osx CRXTYPE=nfs \
SPOOLDIR=/var/spool MAILSPOOL=/var/mail \
- BASECFLAGS="-g -O"
+ BASECFLAGS="-g -O" \
+ EXTRALDFLAGS="-framework DirectoryService"
ptx: # PTX
$(BUILD) `$(CAT) SPECIALS` OS=$@ \
diff -Nrup imap-2002e/src/osdep/unix/ckp_osx.c
/usr/local/src/imap-2002e/src/osdep/unix/ckp_osx.c
--- imap-2002e/src/osdep/unix/ckp_osx.c Wed Dec 31 19:00:00 1969
+++ /usr/local/src/imap-2002e/src/osdep/unix/ckp_osx.c Tue Oct 28 20:35:58 2003
@@ -0,0 +1,471 @@
+/*
+ * Program: OS X check password
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: [EMAIL PROTECTED]
+ *
+ * Modified by: Tom Brown
+ * [EMAIL PROTECTED]
+ * (Modifications provided as-is, with no warranties, including the
warranty
+ * of merchantability or fitness for a particular purpose.)
+ *
+ * Date: 1 August 1988
+ * Last Edited: October 26, 2003
+ *
+ * The IMAP toolkit provided in this Distribution is
+ * Copyright 2000 University of Washington.
+ * The full text of our legal notices is contained in the file called
+ * CPYRIGHT, included with this Distribution.
+ *
+ * This file contains code adapted from Apple's sample code. The following notice
applies
+ * to the Apple-derived portion of this file.
+ *
+ Copyright: � 2000-2001 by Apple Computer, Inc., all rights reserved.
+
+ IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc.
("Apple") in
+ consideration of your agreement to the following terms, and your use,
installation,
+ modification or redistribution of this Apple software constitutes acceptance
of these
+ terms. If you do not agree with these terms, please do not use, install,
modify or
+ redistribute this Apple software.
+
+ In consideration of your agreement to abide by the following terms, and
subject to these
+ terms, Apple grants you a personal, non-exclusive license, under Apple�s
copyrights in
+ this original Apple software (the "Apple Software"), to use, reproduce, modify
and
+ redistribute the Apple Software, with or without modifications, in source
and/or binary
+ forms; provided that if you redistribute the Apple Software in its entirety
and without
+ modifications, you must retain this notice and the following text and
disclaimers in all
+ such redistributions of the Apple Software. Neither the name, trademarks,
service marks
+ or logos of Apple Computer, Inc. may be used to endorse or promote products
derived from
+ the Apple Software without specific prior written permission from Apple.
Except as expressly
+ stated in this notice, no other rights or licenses, express or implied, are
granted by Apple
+ herein, including but not limited to any patent rights that may be infringed
by your
+ derivative works or by other works in which the Apple Software may be
incorporated.
+
+ The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO
WARRANTIES,
+ EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
NON-INFRINGEMENT,
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE
SOFTWARE OR ITS
+ USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
+
+ IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS
+ OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF
THE USE,
+ REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER
CAUSED AND
+ WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT
LIABILITY OR
+ OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#include <CoreServices/CoreServices.h>
+#include <DirectoryService/DirServices.h>
+#include <DirectoryService/DirServicesUtils.h>
+#include <DirectoryService/DirServicesConst.h>
+
+ // Liberally borrowed from Apple's Open Directory SDK
+
+typedef enum {
+ // NULL allocation errors
+ kErrDataBufferAllocate = 1,
+ kErrDataNodeAllocateBlock,
+ kErrDataNodeAllocateString,
+ kErrDataListAllocate,
+ kErrBuildFromPath,
+ kErrGetPathFromList,
+ kErrBuildListFromNodes,
+ kErrBuildListFromStrings,
+ kErrBuildListFromStringsAlloc,
+ kErrDataListCopyList,
+ kErrAllocAttributeValueEntry,
+
+ // Error associations
+ kErrOpenDirSrvc,
+ kErrCloseDirSrvc,
+ kErrDataListDeallocate,
+ kErrDataBufferDeAllocate,
+ kErrFindDirNodes,
+ kErrOpenRecord,
+ kErrCreateRecordAndOpen,
+ kErrCloseRecord,
+ kErrDeleteRecord,
+ kErrDataNodeDeAllocate,
+
+ kErrDataBuffDealloc,
+ kErrCreateRecord,
+ kErrAddAttribute,
+ kErrAddAttributeValue,
+ kErrSetAttributeValue,
+ kErrGetRecAttrValueByIndex,
+ kErrGetDirNodeName,
+ kErrOpenDirNode,
+ kErrCloseDirNode,
+ kErrGetRecordList,
+
+ kErrGetRecordEntry,
+ kErrGetAttributeEntry,
+ kErrGetAttributeValue,
+ kErrDeallocAttributeValueEntry,
+ kErrDoDirNodeAuth,
+ kErrGetRecordNameFromEntry,
+ kErrGetRecordTypeFromEntry,
+ kErrGetRecordAttributeInfo,
+ kErrGetRecordReferenceInfo,
+ kErrGetDirNodeInfo,
+
+ kErrGetDirNodeCount,
+ kErrGetDirNodeList,
+ kErrRemoveAttributeValue,
+ kErr,
+
+ kErrMemoryAlloc,
+ kErrEmptyDataBuff,
+ kErrEmptyDataParam,
+ kErrBuffTooSmall,
+ kErrMaxErrors,
+ kUnknownErr = 0xFF
+} eErrCodes;
+
+long SetUpAuthBuffs ( tDirReference dsRef, tDataBuffer **outAuthBuff,
+ unsigned long inAuthBuffSize,
+ tDataBuffer **outStepBuff,
+ unsigned long inStepBuffSize,
+ tDataBuffer **outTypeBuff,
+ const char *inAuthMethod )
+{
+ long error = eDSNoErr;
+ long error2 = eDSNoErr;
+
+ if ( (outAuthBuff == nil) || (outStepBuff == nil) ||
+ (outTypeBuff == nil) || (inAuthMethod == nil) )
+ {
+ return( kErrEmptyDataParam );
+ }
+
+ *outAuthBuff = dsDataBufferAllocate( dsRef, inAuthBuffSize );
+ if ( *outAuthBuff != nil )
+ {
+ *outStepBuff = dsDataBufferAllocate( dsRef, inStepBuffSize );
+ if ( *outStepBuff != nil )
+ {
+ *outTypeBuff = dsDataNodeAllocateString( dsRef, inAuthMethod );
+ if ( *outTypeBuff == nil )
+ {
+ error = kErrDataNodeAllocateString;
+ }
+ }
+ else
+ {
+ error = kErrDataBufferAllocate;
+ }
+ }
+ else
+ {
+ error = kErrDataBufferAllocate;
+ }
+
+ if ( error != eDSNoErr )
+ {
+ if ( *outAuthBuff != nil )
+ {
+ error2 = dsDataBufferDeAllocate( dsRef, *outAuthBuff );
+ }
+
+ if ( *outStepBuff != nil )
+ {
+ error2 = dsDataBufferDeAllocate( dsRef, *outStepBuff );
+ }
+
+ if ( *outTypeBuff != nil )
+ {
+ error2 = dsDataBufferDeAllocate( dsRef, *outTypeBuff );
+ }
+ }
+
+ return( error );
+
+} // SetUpAuthBuffs
+
+long FillAuthBuff ( tDataBuffer *inAuthBuff, unsigned long inCount, unsigned long
inLen, void *inData, ... )
+{
+ long error = eDSNoErr;
+ unsigned long curr = 0;
+ unsigned long buffSize = 0;
+ unsigned long count = inCount;
+ unsigned long len = inLen;
+ void *data = inData;
+ bool firstPass = true;
+ char *p = nil;
+ va_list args;
+
+ // If the buffer is nil, we have nowhere to put the data
+ if ( inAuthBuff == nil )
+ {
+ return( kErrEmptyDataBuff );
+ }
+
+ // If the buffer is nil, we have nowhere to put the data
+ if ( inAuthBuff->fBufferData == nil )
+ {
+ return( kErrEmptyDataBuff );
+ }
+
+ // Make sure we have data to copy
+ if ( (inLen != 0) && (inData == nil) )
+ {
+ return( kErrEmptyDataParam );
+ }
+
+ // Get buffer info
+ p = inAuthBuff->fBufferData;
+ buffSize = inAuthBuff->fBufferSize;
+
+ // Set up the arg list
+ va_start( args, inLen );
+
+ while ( count-- > 0 )
+ {
+ if ( !firstPass )
+ {
+ len = va_arg( args, unsigned long );
+ data = va_arg( args, void * );
+ }
+
+ if ( (curr + len) > buffSize )
+ {
+ // This is bad, lets bail
+ return( kErrBuffTooSmall );
+ }
+
+ memcpy( &(p[ curr ]), &len, sizeof( long ) );
+ curr += sizeof( long );
+
+ if ( len > 0 )
+ {
+ memcpy( &(p[ curr ]), data, len );
+ curr += len;
+ }
+ firstPass = false;
+ }
+
+ inAuthBuff->fBufferLength = curr;
+
+ return( error );
+
+} // FillAuthBuff
+
+
+long DoClearTextAuth ( tDirReference dsRef, tDirNodeReference inNode, char *inName,
char *inPasswd )
+{
+ long error= eDSNoErr;
+ long error2= eDSNoErr;
+ tDataBuffer *pAuthBuff= nil;
+ tDataBuffer *pStepBuff= nil;
+ tDataNode *pAuthType= nil;
+
+ error = SetUpAuthBuffs( dsRef, &pAuthBuff, 2048, &pStepBuff, 2048, &pAuthType,
kDSStdAuthNodeNativeClearTextOK );
+ if ( error == eDSNoErr )
+ {
+ error = FillAuthBuff ( pAuthBuff, 2,
+ (long)strlen( inName ), inName,
+ (long)strlen( inPasswd ), inPasswd );
+
+ if ( error == eDSNoErr )
+ {
+ error = dsDoDirNodeAuth( inNode, pAuthType, true, pAuthBuff, pStepBuff,
nil );
+ }
+
+ error2 = dsDataBufferDeAllocate( dsRef, pAuthBuff );
+ error2 = dsDataBufferDeAllocate( dsRef, pStepBuff );
+ error2 = dsDataBufferDeAllocate( dsRef, pAuthType );
+ }
+
+ return( error );
+
+} // DoClearTextAuth
+
+static long OpenDirNode ( tDirReference dsRef, char *inNodeName, tDirNodeReference
*outNodeRef )
+{
+ long error = eDSNoErr;
+ long error2 = eDSNoErr;
+ tDataList *pDataList = nil;
+
+ pDataList = dsBuildFromPath( dsRef, inNodeName, "/" );
+ if ( pDataList != nil )
+ {
+ error = dsOpenDirNode( dsRef, pDataList, outNodeRef );
+ error2 = dsDataListDeallocate( dsRef, pDataList );
+ }
+ else
+ {
+ error = kErrBuildFromPath;
+ }
+
+ return( error );
+
+} // OpenDirNode
+
+static long FindDirectoryNodes ( tDirReference dsRef,
+ char *inNodeName,
+ tDirPatternMatch inMatch,
+ char **outNodeName)
+{
+ long error = eDSNoErr;
+ long error2 = eDSNoErr;
+ bool done = false;
+ unsigned long uiCount = 0;
+ unsigned long uiIndex = 0;
+ tDataList *pNodeNameList = nil;
+ tDataList *pDataList = nil;
+ char *pNodeName = nil;
+ tDataBuffer *buf;
+
+ if ( inNodeName != nil )
+ {
+ pNodeNameList = dsBuildFromPath( dsRef, inNodeName, "/" );
+ if ( pNodeNameList == nil )
+ {
+ return( kErrDataNodeAllocateString );
+ }
+ }
+
+ buf = dsDataBufferAllocate( dsRef, 1024);
+ do {
+ error = dsFindDirNodes( dsRef, buf, pNodeNameList, inMatch,
&uiCount, nil );
+ if ( error == eDSBufferTooSmall )
+ {
+ unsigned long buffSize = buf->fBufferSize;
+ dsDataBufferDeAllocate( dsRef, buf );
+ buf = nil;
+ buf = dsDataBufferAllocate( dsRef, buffSize * 2 );
+ }
+ } while ( error == eDSBufferTooSmall );
+ if ( error == eDSNoErr )
+ {
+
+ if ( uiCount != 0 )
+ {
+ pDataList = dsDataListAllocate( dsRef );
+ if ( pDataList != nil )
+ {
+ for ( uiIndex = 1; (uiIndex <= uiCount) &&
(error == eDSNoErr); uiIndex++ )
+ {
+ error = dsGetDirNodeName( dsRef, buf,
uiIndex, &pDataList );
+ if ( error == eDSNoErr )
+ {
+ pNodeName = dsGetPathFromList(
dsRef, pDataList, "/" );
+ if ( pNodeName != nil )
+ {
+
+ if ( (outNodeName !=
nil) && !done )
+ {
+ *outNodeName =
pNodeName;
+ done = true;
+ }
+ else
+ {
+ free(
pNodeName );
+ pNodeName =
nil;
+ }
+
+ error2 =
dsDataListDeallocate( dsRef, pDataList );
+ }
+ else
+ {
+ error =
kErrGetPathFromList;
+ }
+ }
+ }
+ }
+ else
+ {
+ error = kErrDataListAllocate;
+ }
+ }
+ }
+
+ if ( pNodeNameList != nil )
+ {
+ error2 = dsDataListDeallocate( dsRef, pNodeNameList );
+ }
+
+ dsDataBufferDeAllocate( dsRef, buf );
+ return( error );
+
+} // FindDirectoryNodes
+
+static long MyOpenDirNode ( tDirReference dirRef,
+ tDirNodeReference *outNodeRef )
+{
+ long siStatus = noErr;
+ char *pNodeName = nil;
+
+ // siStatus = OpenDirectoryServices();
+ if ( siStatus != noErr )
+ {
+ return( siStatus );
+ }
+
+ // Find and open local node
+ siStatus = FindDirectoryNodes( dirRef, nil, eDSLocalNodeNames, &pNodeName );
+ if ( siStatus == noErr )
+ {
+ siStatus = OpenDirNode( dirRef, pNodeName, outNodeRef );
+
+ free( pNodeName );
+ pNodeName = nil;
+ }
+
+ return siStatus;
+}
+
+/* Check password
+ * Accepts: login passwd struct
+ * password string
+ * argument count
+ * argument vector
+ * Returns: passwd struct if password validated, NIL otherwise
+ */
+
+struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[])
+{
+ if (pw->pw_passwd && pw->pw_passwd[0] && pw->pw_passwd[1])
+ {
+ long dirStatus = eDSNoErr;
+ tDirNodeReference nodeRef = NULL;
+ tDirReference dirRef = NULL;
+
+ dirStatus = dsOpenDirService( &dirRef );
+ if ( dirStatus == eDSNoErr )
+ {
+ dirStatus = MyOpenDirNode( dirRef, &nodeRef );
+ }
+
+ if (dirStatus == eDSNoErr)
+ {
+ dirStatus = DoClearTextAuth(dirRef, nodeRef, pw->pw_name, pass);
+ }
+
+ // Any error causes us to fail (including failure of authentication, above)
+ if (dirStatus != eDSNoErr)
+ {
+ pw = NULL;
+ }
+
+ if (nodeRef)
+ {
+ dsCloseDirNode( nodeRef );
+ }
+
+ if ( dirRef != NULL )
+ {
+ dirStatus = dsCloseDirService( dirRef );
+ }
+
+ return pw;
+ }
+ else
+ {
+ return NULL;
+ }
+}