/******************************************************************************
*   Source File Name: loginAsService.c
*   %version: 10 %
*   %date_modified: Wed Jan 20 11:25:16 1999 %
*

  Copyright (c) 1989-1997 Novell, Inc.  All Rights Reserved.                      

  THIS WORK IS AN UNPUBLISHED WORK AND CONTAINS CONFIDENTIAL PROPRIETARY
  AND TRADE SECRET INFORMATION OF NOVELL, INC. ACCESS  TO  THIS  WORK IS
  RESTRICTED TO (I) NOVELL, INC.  EMPLOYEES WHO HAVE A NEED TO  KNOW HOW
  TO  PERFORM  TASKS WITHIN  THE SCOPE  OF  THEIR   ASSIGNMENTS AND (II)
  ENTITIES OTHER  THAN  NOVELL, INC.  WHO  HAVE ENTERED INTO APPROPRIATE 
  LICENSE   AGREEMENTS.  NO  PART  OF  THIS WORK MAY BE USED, PRACTICED,
  PERFORMED COPIED, DISTRIBUTED, REVISED, MODIFIED, TRANSLATED, ABRIDGED,
  CONDENSED, EXPANDED, COLLECTED, COMPILED, LINKED,  RECAST, TRANSFORMED
  OR ADAPTED  WITHOUT THE PRIOR WRITTEN CONSENT OF NOVELL, INC.  ANY USE
  OR EXPLOITATION  OF  THIS WORK WITHOUT AUTHORIZATION COULD SUBJECT THE
  PERPETRATOR  TO CRIMINAL AND CIVIL LIABILITY.$



 *****************************************************************************/

#ifndef _STATIC_
#define _STATIC_ static
#else
#undef _STATIC_
#define _STATIC_
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unicode.h>

#if USE_DCLIENT
/* The purpose of this is to choose between DClient and Clib APIs for NDS
 */
#include <ddsbase.h>
#include <ddsapi.h>
#include <dconst.h>
#include <ddsserv.h>
#include <ddsadv.h>
#include <process.h>
#else 
// Include the CLIB headers for relevant calls
#include	<nwnet.h>
#include	<nwdsapi.h>
#endif


#ifdef WIN32
#include <sys/timeb.h>
#include <time.h>
#endif
#ifdef UNIX
#include <sys/types.h>
#include <sys/time.h>    
#include <unistd.h>
#endif
#ifdef N_PLAT_NLM
#include <time.h>
#include <process.h>
#include <nwthread.h>
#include <signal.h>
#include <nwlocale.h>
#include <unicode.h>
#include <nwtoolib.h>
#include <nwstring.h>
#include <nwdserr.h>
#endif

#include "loginAsService.h"
#define LOCAL_PASSWORD_LEN 31

static void generatePassword( char *password )
{
#ifdef WIN32
   struct _timeb timebuffer;
	int	seed;
	int	r1, r2, r3;

	/* Obtain a random seed.  This is based on the current time plus
	 * a millisecond count after sleeping for 2 milliseconds.  We assume
	 * that a degree of randomness is obtained since the system is unlikly to
	 * awaken us exactly on time after a sleep .
	 */

	seed = (unsigned)time(0);
   _ftime( &timebuffer );
	seed = (seed << 4) + timebuffer.millitm;
	Sleep(2 /* Time in milliseconds*/);
   _ftime( &timebuffer );
	seed = (seed << 4) + timebuffer.millitm;

	srand(seed);
	r1 = rand();
	r2 = rand();
	r3 = rand();
	sprintf(password, "%x%d%x", r3, r1, r2);
#endif
#ifdef UNIX
	int	seed;
	int 	r1, r2, r3;
	struct timeval t;

	gettimeofday(&t, NULL);
	seed = t.tv_usec + t.tv_sec;
	sleep(1 /* one second */);
	gettimeofday(&t, NULL);
	seed = (seed << 4) + t.tv_usec + t.tv_sec;

	srand(seed);
	r1 = rand();
	r2 = rand();
	r3 = rand();
	sprintf(password, "%x%d%x", r3, r1, r2);
#endif
#ifdef N_PLAT_NLM
	int	seed;
	int 	r1, r2, r3;

	seed = clock();
	delay( 1053 /* in milliseconds */);
	seed = (seed << 4) + clock(); 

	srand(seed);
	r1 = rand();
	r2 = rand();
	r3 = rand();
	sprintf(password, "%x%d%x", r3, r1, r2);
#endif
}



#if USE_DCLIENT


/**
 * Verify if the class of the object is the same as or a subclass of objectClass
 */
#define VALUE_SIZE	1024
#define _ALLOC_ON_STACK

const	unicode attrib[] = {'O','b','j','e','c','t',' ','C','l','a','s','s', 0};
const	unicode *attribList[] = {attrib, 0};
unicode SERVICE_CLASS_NAME_UNICODE[] 	= {'S', 'A', 'S', ':','S','e','r','v','i','c','e',0};

_STATIC_ int VerifyObjectClass(int context, unicode * objectClass)
{
	int	err;
	DDCReadFilter	readFilter;
	DDCVALUE 		*value, *valuePtr;
	unsigned		i, valueCount=0;
	nuint8 			verified = 0;

#ifdef _ALLOC_ON_STACK	
	char 			buffer[VALUE_SIZE];
	value			= (DDCVALUE *)buffer;
#else
	if (!(value = (DDCVALUE *)malloc(VALUE_SIZE)))
		return ERR_INSUFFICIENT_MEMORY;
#endif

	memset(&readFilter, 0, sizeof(readFilter));
	readFilter.attributes = (DDCString **)attribList;

	do {
		if (err = DDCReadToBuffer(context, &readFilter, DS_ATTRIBUTE_VALUES,
		          			VALUE_SIZE, VALUE_SIZE, value, &valueCount))
			break;
			
		for (i=0, valuePtr=value; i<valueCount; i++, valuePtr++) {		
			
			if ((!verified) &&
				(valuePtr->syntax == SYN_CLASS_NAME) &&
				(!memcmp((void *)valuePtr->attrName, (void *)attrib, sizeof(attrib))) &&
				(!memcmp((void *)objectClass, (void *)valuePtr->data, valuePtr->size))) {
					verified = 1;
					break;
			}
		}				
    } while ((!err)  && valueCount);
	
	if (!err && !verified) err = ERR_OBJECT_CLASS_VIOLATION;

#ifndef _ALLOC_ON_STACK	
	free(value);
#endif

	return err;
}


int DDCLoginAsService(int context, unicode *serviceDN)
{
	int err;
	int cntRetries = 3;
	int dupContext;
	char	password[LOCAL_PASSWORD_LEN+1];

	static const uint32  flags = DDC_UNICODE_STRINGS;

	if (err = DDCDuplicateContext(context, &dupContext))
		goto ERROR_1;

	// Set the desired context settings, reset the other flags
	if (err = DDCSetContextFlags(dupContext, flags, ~flags))
		goto ERROR_2;
		
	if (err = DDSLoginAsServer(dupContext))
		goto ERROR_2;
	
	if (err = DDCResolveName(dupContext, DS_WRITABLE | DS_DEREFERENCE_ALIASES, serviceDN))
		goto ERROR_3;

	if (err = DDCAuthenticateConnection(dupContext))
		goto ERROR_3;

	if (err = VerifyObjectClass(dupContext, SERVICE_CLASS_NAME_UNICODE))
		goto ERROR_3;

	generatePassword(password);
	err = DDCGenerateKeyPair(dupContext, password);

ERROR_3:
	DDCLogout(dupContext);		// Relinquish server rights ASAP
ERROR_2:
	DDCFreeContext(dupContext);
ERROR_1:
	if (err) goto ERROR_0;
	

	/* Now login with that password -- retry for one half minute 
    * to allow for replication 
    */
	if (err = DDCResolveName(context, DS_WRITABLE | DS_DEREFERENCE_ALIASES, serviceDN))
		goto ERROR_3;
	while (cntRetries-- > 0) {
		if (!(err = DDCLogin(context, password)))
			break;
#ifdef WIN32
		Sleep(10*1000 /* Time in milliseconds*/);
#endif
#ifdef UNIX
		sleep(10 /* Time in seconds*/);
#endif
#ifdef N_PLAT_NLM
		ThreadSwitchWithDelay();
#endif
	}
	if (err)
		goto ERROR_0; 

	err = DDCAuthenticateConnection(context);

ERROR_0:
	return err; 

}


#else /* DCLIENT IS NOT DEFINED: Use CLIB */

/**
 * Verify if the class of the objectName is the same as or a subclass of objectClass
 */
 
#ifdef N_PLAT_NLM


const	char 	OBJECT_CLASS[] 	= 
					{'O','b','j','e','c','t',' ','C','l','a','s','s', 0};
const	char 	SERVICE_CLASS_NAME[] 	= 
					{'S', 'A', 'S', ':','S','e','r','v','i','c','e',0};

const	unicode 	UOBJECT_CLASS[] 	= 
					{'O','b','j','e','c','t',' ','C','l','a','s','s', 0};
const	unicode 	USERVICE_CLASS_NAME[] 	= 
					{'S', 'A', 'S', ':','S','e','r','v','i','c','e',0};

_STATIC_ int VerifyObjectClass(NWDSContextHandle   context, pnstr8 objectName)
{
	NWCCODE	err;
	Buf_T *nameBuf, *objInfo;
	NWDS_ITERATION iteration;
	NWCOUNT i, j, attrCount, attrValCount;
	char attrName[MAX_DN_BYTES], attrVal[MAX_SCHEMA_NAME_BYTES];
	NWSYNTAX_ID syntaxID;
	nuint8 verified = 0;
	nint32	flags = 0;
	
	pnstr8 	className;
	pnstr8	objectClass;
	
 	if (err = NWDSGetContext(context, DCK_FLAGS, &flags))
 		goto ERROR_1;

 	if (flags & DCV_XLATE_STRINGS) {
		className	= (pnstr8)OBJECT_CLASS;
		objectClass = (pnstr8)SERVICE_CLASS_NAME;
	} else {
		className	= (pnstr8)UOBJECT_CLASS;
		objectClass = (pnstr8)USERVICE_CLASS_NAME;
	}	
	
 	
	if (err = NWDSAllocBuf(DEFAULT_MESSAGE_LEN, &nameBuf)) 
		goto ERROR_1;
	if (err = NWDSAllocBuf(DEFAULT_MESSAGE_LEN, &objInfo))
		goto ERROR_2;

	if ((err = NWDSInitBuf(context, DSV_READ, nameBuf)) ||
		(err = NWDSPutAttrName(context, nameBuf, className)))
		goto ERROR_3;
		
	iteration = NO_MORE_ITERATIONS;
	
	do {
		if ((err = NWDSRead(context, objectName, DS_ATTRIBUTE_VALUES,
		          			FALSE, nameBuf, &iteration, objInfo)) ||
		    (err = NWDSGetAttrCount(context, objInfo, &attrCount)))
			break;
			
		for (i=0; i<attrCount; i++) {		
			if  (err = NWDSGetAttrName(context, objInfo, attrName,
    	                              &attrValCount, &syntaxID))
			    break;
			for (j=0; j<attrValCount; j++) {
				if (err = NWDSGetAttrVal(context, objInfo, syntaxID, 
							(syntaxID == SYN_CLASS_NAME) ? attrVal : NULL))
					break;
				if ((syntaxID == SYN_CLASS_NAME) &&
					(((flags & DCV_XLATE_STRINGS) && 
					  (!strcmp((void *)attrName, className)) &&
					  (!strcmp((const char *)objectClass, (const char *)attrVal))
					 ) || (
					  (!uniicmp((unicode *)attrName, (unicode *)className)) &&
					  (!uniicmp((unicode *)objectClass, (unicode *)attrVal))
					 ))) {
					 
					verified = 1;
					break;
				}
			}
			if (err || verified) break;
		}				
    } while ((!err) && (!verified) && (iteration != NO_MORE_ITERATIONS));
	
	if (!err && !verified) err = ERR_OBJECT_CLASS_VIOLATION;

ERROR_3:
	NWDSFreeBuf(objInfo);
ERROR_2:	
	NWDSFreeBuf(nameBuf);
ERROR_1:	
	return err;
}


int NWDSLoginAsService (
	   NWDSContextHandle   context,
	   nflag32             optionsFlag,
	   pnstr8              serviceDN,
	   nuint32             validityPeriod)
{
	int err;
	int cntRetries = 3;
	NWDSContextHandle dupContext;
	NWCONN_HANDLE	  conn;
	nuint32           objectID;
	char	password[LOCAL_PASSWORD_LEN+1];

	if (err = NWDSDuplicateContextHandle(context, &dupContext))
		goto ERROR_1;

	if (err = NWDSLoginAsServer(dupContext))
		goto ERROR_2;
	
	if (err = NWDSResolveName(dupContext, serviceDN, &conn, &objectID))
		goto ERROR_3;

	if (err = NWDSAuthenticateConn(dupContext, conn))
		goto ERROR_3;

	if (err = VerifyObjectClass(dupContext, serviceDN))
		goto ERROR_3;
		
	generatePassword(password);
	err = NWDSGenerateObjectKeyPair(dupContext, serviceDN, password, 0);

ERROR_3:
	NWDSLogout(dupContext);		// Relinquish server rights ASAP
ERROR_2:
	NWDSFreeContext(dupContext);
ERROR_1:
	if (err) goto ERROR_0;
	

	/* Now login with that password -- retry for one half minute 
    * to allow for replication 
    */
	if (err = NWDSResolveName(context, serviceDN, &conn, &objectID))
		goto ERROR_3;
	while (cntRetries-- > 0) {
		if (!(err = NWDSLogin(context, 0, serviceDN, password, validityPeriod)))
			break;
#ifdef WIN32
		Sleep(10*1000 /* Time in milliseconds*/);
#endif
#ifdef UNIX
		sleep(10 /* Time in seconds*/);
#endif
#ifdef N_PLAT_NLM
		ThreadSwitchWithDelay();
#endif
	}
	if (err)
		goto ERROR_0; 

	err = NWDSAuthenticateConn(context, conn);

ERROR_0:
	return err; /* SUCCESS */


}

#else /* N_PLAT_NLM */

#define ERR_INCOMPATIBLE_OS						-753		/* 0xFFFFFD0F */

int NWDSLoginAsService (
	   NWDSContextHandle   context,
	   nflag32             optionsFlag,
	   pnstr8              serviceDN,
	   nuint32             validityPeriod)
{
	return ERR_INCOMPATIBLE_OS;
}

#endif /* N_PLAT_NLM */
   
#endif

#ifdef _MAKE_LIBRARY_NLM
#ifdef N_PLAT_NLM
void main(int argc, char **argv)
{
	ExitThread (TSR_THREAD,0);
}
#endif
#endif

