#include <pwd.h>
#include <stddef.h> // for offsetof()
#include <stdlib.h> // for malloc()
#include <string.h> // for strcmp()
#include <time.h>
#include <unistd.h> // for crypt()
#include <mach/mach.h>
#include <mach/mach_error.h>
#include <mach/message.h>
#include <servers/bootstrap.h>
#include "checkpw.h"
typedef struct {
unsigned int msgt_name : 8,
msgt_size : 8,
msgt_number : 12,
msgt_inline : 1,
msgt_longform : 1,
msgt_deallocate : 1,
msgt_unused : 1;
} mach_msg_type_t;
typedef struct sObject
{
unsigned long type;
unsigned long count;
unsigned long offset;
unsigned long used;
unsigned long length;
} sObject;
typedef struct sComData
{
mach_msg_header_t head;
mach_msg_type_t type;
unsigned long fDataSize;
unsigned long fDataLength;
unsigned long fMsgID;
unsigned long fPID;
unsigned long fPort;
unsigned long fIPAddress;
sObject obj[ 10 ];
char data[ 1 ];
} sComData;
#define kMsgBlockSize (1024 * 4) // Set to average of 4k
#define kObjSize (sizeof( sObject ) * 10) // size of object struct
#define kIPCMsgLen kMsgBlockSize // IPC message block size
#define kIPCMsgSize sizeof( sIPCMsg )
typedef struct sIPCMsg
{
mach_msg_header_t fHeader;
unsigned long fMsgType;
unsigned long fCount;
unsigned long fOf;
unsigned long fMsgID;
unsigned long fPID;
unsigned long fPort;
sObject obj[ 10 ];
char fData[ kIPCMsgLen ];
mach_msg_security_trailer_t fTail;
} sIPCMsg;
typedef enum {
kResult = 4460,
ktDataBuff = 4466,
} eValueType;
enum eDSServerCalls {
kCheckUserNameAndPassword = 9
};
int checkpw_internal( const struct passwd* pw, const char* password );
int checkpw( const char* userName, const char* password )
{
struct passwd* pw = NULL;
int status;
if (!userName)
return CHECKPW_UNKNOWNUSER;
pw = getpwnam( userName );
if (pw == NULL)
return CHECKPW_UNKNOWNUSER;
status = checkpw_internal(pw, password);
endpwent();
return status;
}
int checkpw_internal( const struct passwd* pw, const char* password )
{
int siResult = CHECKPW_FAILURE;
kern_return_t result = err_none;
mach_port_t bsPort = 0;
mach_port_t serverPort = 0;
mach_port_t replyPort = 0;
const char *const srvrName = "DirectoryService";
sIPCMsg* msg = NULL;
unsigned long len = 0;
long curr = 0;
unsigned long i = 0;
do {
if (pw->pw_passwd == NULL || pw->pw_passwd[0] == '\0') {
if (password == NULL || password[0] == '\0')
siResult = CHECKPW_SUCCESS;
else
siResult = CHECKPW_BADPASSWORD;
break;
}
if (!password)
{
siResult = CHECKPW_BADPASSWORD;
break;
}
if (strcmp(crypt(password, pw->pw_passwd), pw->pw_passwd) == 0) {
siResult = CHECKPW_SUCCESS;
break;
}
result = mach_port_allocate( mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &replyPort );
if ( result != err_none ) {
siResult = CHECKPW_FAILURE;
break;
}
result = task_get_bootstrap_port( mach_task_self(), &bsPort );
if ( result != err_none ) {
siResult = CHECKPW_FAILURE;
break;
}
result = bootstrap_look_up( bsPort, (char *)srvrName, &serverPort );
if ( result != err_none ) {
siResult = CHECKPW_FAILURE;
break;
}
msg = calloc( sizeof( sIPCMsg ), 1 );
if ( msg == NULL ) {
siResult = CHECKPW_FAILURE; break;
}
msg->obj[0].type = ktDataBuff;
msg->obj[0].count = 1;
msg->obj[0].offset = offsetof(struct sComData, data);
len = strlen( pw->pw_name );
if (curr + len + sizeof(unsigned long) > kIPCMsgLen)
{
siResult = CHECKPW_FAILURE;
break;
}
memcpy( &(msg->fData[ curr ]), &len, sizeof( unsigned long ) );
curr += sizeof( unsigned long );
memcpy( &(msg->fData[ curr ]), pw->pw_name, len );
curr += len;
len = strlen( password );
if (curr + len + sizeof(unsigned long) > kIPCMsgLen)
{
siResult = CHECKPW_FAILURE;
break;
}
memcpy( &(msg->fData[ curr ]), &len, sizeof( unsigned long ) );
curr += sizeof ( unsigned long );
memcpy( &(msg->fData[ curr ]), password, len );
curr += len;
msg->obj[0].used = curr;
msg->obj[0].length = curr;
msg->fHeader.msgh_bits = MACH_MSGH_BITS( MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND );
msg->fHeader.msgh_size = sizeof( sIPCMsg ) - sizeof( mach_msg_security_trailer_t );
msg->fHeader.msgh_id = kCheckUserNameAndPassword;
msg->fHeader.msgh_remote_port = serverPort;
msg->fHeader.msgh_local_port = replyPort;
msg->fMsgType = kCheckUserNameAndPassword;
msg->fCount = 1;
msg->fOf = 1;
msg->fPort = replyPort;
msg->fPID = getpid();
msg->fMsgID = time( NULL ) + kCheckUserNameAndPassword;
result = mach_msg_send( (mach_msg_header_t*)msg );
if ( result != MACH_MSG_SUCCESS ) {
siResult = CHECKPW_FAILURE;
break;
}
memset( msg, 0, kIPCMsgLen );
result = mach_msg( (mach_msg_header_t *)msg, MACH_RCV_MSG | MACH_RCV_TIMEOUT,
0, kIPCMsgSize, replyPort, 300 * 1000, MACH_PORT_NULL );
if ( result != MACH_MSG_SUCCESS ) {
siResult = CHECKPW_FAILURE;
break;
}
if ( msg->fCount != 1 ) {
siResult = CHECKPW_FAILURE;
break;
}
for (i = 0; i < 10; i++ )
{
if ( msg->obj[ i ].type == (unsigned long)kResult )
{
siResult = msg->obj[ i ].count;
break;
}
}
} while (0);
if (msg != NULL) {
free(msg);
msg = NULL;
}
mach_port_deallocate( mach_task_self(), serverPort);
if ( replyPort != 0 )
mach_port_destroy( mach_task_self(), replyPort );
return siResult;
}