/*
 * crp.c
 * spd@daphne.cps.unizar.es
 *
 * crypt/compare strings using serveral standard methods
 *
 * - crypt (unix standard)
 * - crypt16 (Tru64) (other: define MY_CRYPT16, link modified libufc)
 * - bigcrypt (HP-UX, Tru64) (other: define MY_BIGCRYPT)
 * - md5 (linux) (other: define USE_MD5, link md5crypt)
 * - LanManager/NT (define USE_SMB, link samba 2.0.9)
 *
 *
 * For SunOS 4.1.x, use gcc
 * For __convexc__, link with libufc
 * For linux, if "undefined reference to `crypt'" link with -lcrypt

 	gcc -DMY_BIGCRYPT -DUSE_BIGCRYPT -o crp crp.c -lcrypt

	for linux, if you want to use crypt16, you have to rename
	crypt function in ufc-crypt/crypt_util.c to Ucrypt (for instance)
	See Makefile

 * For HP-UX, if "Unsatisfied symbols: bigcrypt" link with -lsec

 	cc -Ae -o crp crp.c -lsec
 	cc -Aa -D_HPUX_SOURCE -o crp crp.c -lsec

 * For Tru64, if "ld: Unresolved: bigcrypt" link with -lsecurity

 	cc -DUSE_BIGCRYPT -o crp crp.c -lsecurity
 	
 *
 * For Solaris 2.x or IRIX 6.x

 	cc -I/usr/local/include -DUSE_CRYPT16 -DMY_CRYPT16 \
	-DMY_BIGCRYPT -DUSE_BIGCRYPT  crp.c  -o crp -L/usr/local/lib -lufc

 *
 */

static char __ident[] = "@(#)(c) SPDsoft, 0.0 May 24 2001";
#define VERS_STR        ((char*)&__ident[4])

#if ( defined __sun__ && !defined __svr4__ )
#	define _BSD_
#endif

#if defined __linux__
#	define _XOPEN_SOURCE
#	ifndef NO_MD5
#		define USE_MD5
#	endif
#endif

#if defined _HPUX_SOURCE
#	ifndef NO_BIGCRYPT
#		define USE_BIGCRYPT
#	endif
/*
      HP-UX 11 bigcrypt(3C)

      bigcrypt() acts like crypt(3C), but handles much larger strings.
      bigcrypt takes the segments of cleartext and encrypts them
      individually, at first using the salt passed in, and then using the
      first two characters of the previous encrypted segment as the salt for
      the next segment.  This avoids duplicated ciphertext chunks when the
      password characters are repeated, so that the encryption of a segment
      involves the encryption of all the previous segments.

      Each chipertext segment is concatenated, with the salt at the
      beginning, to form the entire encrypted string.
*/
#endif

#if defined __osf__
/*
  Tru64 5.0 crypt(3)

  The crypt16 subroutine is identical to the crypt function except that it
  will accept a password up to sixteen characters in length. It generates a
  longer encrypted password for use with enhanced security features.
*/
#	ifndef NO_CRYPT16
#		define USE_CRYPT16
#	endif
/*
 * crypt16 will give as second half hash crypt("",salt)+2 if l<=8
 */
#endif

#ifdef USE_MD5
#	ifndef __linux__

/*
 * requires crypt_md5.o and md5.o from md5crypt-1.0
 * http://www.ibiblio.org/pub/Linux/apps/crypto/md5crypt-1.0.tar.gz
 */
		extern char *__md5_crypt(const char *, const char *);
#	endif
#endif


#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/time.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#if !(defined _HPUX_SOURCE) && !(defined __convexc__) && \
!(defined _BSD_) &&  !(defined  __APPLE__ )
#	include <crypt.h>
#endif
#if (defined __convexc__) || (defined _BSD_)
	char *crypt(const char *key, const char *salt);
#endif

#ifdef USE_BIGCRYPT
#	ifndef MY_BIGCRYPT
#		if defined _HPUX_SOURCE
#			include <hpsecurity.h>
#		endif
#		if defined	__osf__
#			include <sys/security.h>
#		endif
#		include <prot.h>
#	else
		char *my_bigcrypt(const char *key, const char *salt);
#		define bigcrypt my_bigcrypt
#	endif
#	ifndef AUTH_MAX_PASSWD_LENGTH
#		define AUTH_MAX_PASSWD_LENGTH          80
#	endif
#endif

#ifdef USE_CRYPT16
#	ifdef MY_CRYPT16
#		include <ufc-crypt.h>
		char *my_crypt16(const char *key, const char *salt);
#		define crypt16 my_crypt16
#	endif
#endif

#ifdef USE_SMB
/*
 *
 * From ENCRYPTION.txt for Samba release 2.0.6
 *  - LanManager encryption
    LanManager encryption is somewhat similar to UNIX password
    encryption. The server uses a file containing a hashed value of a
    user's password.  This is created by taking the user's plaintext
    password, capitalising it, and either truncating to 14 bytes (or
    padding to 14 bytes with null bytes). This 14 byte value is used as
    two 56 bit DES keys to encrypt a 'magic' eight byte value, forming a
    16 byte value which is stored by the server and client. Let this value
    be known as the *hashed password*.

    - Windows NT encryption
    Windows NT encryption is a higher quality mechanism, consisting
    of doing an MD4 hash on a Unicode version of the user's password. This
    also produces a 16 byte hash value that is non-reversible.


 */

#ifdef USE_CRYPT_RN
	char *my_cryptrn(const char *key, const char *salt);
#endif

/*#include "includes.h"*/
#define slprintf snprintf
extern void charset_initialise(void);
extern void codepage_initialise(int client_codepage);
extern void TimeInit(void);
extern int lp_load(const char *pszFname, int, int, int);
extern void nt_lm_owf_gen(char *pwd, unsigned char nt_p16[16], unsigned char p16[16]);

#undef strcpy
#undef strcat

#ifdef  __APPLE__
#define CONFIGFILE "/etc/smb.conf"
#endif 
#ifndef CONFIGFILE
#define CONFIGFILE "/etc/smb/smb.conf"
#endif


char *smb_crypt(const char *key, const char *salt);

#else

extern char *smb_crypt(const char *key, const char *salt);

#endif /* USE_SMB */

extern char *optarg;
extern int optind, opterr, optopt;

void    usage(char *name);
void    fatalError(char *name,char *str);

void    usage(char *name)
{
    fprintf(stderr,
	"usage: %s [-s salt | -c cpass] [-qv] [mode] pass...\n",name);
    fprintf(stderr, "mode:       crypt\n");
#ifdef USE_MD5
    fprintf(stderr, "mode: -m    md5\n");
#endif
#ifdef USE_CRYPT16
    fprintf(stderr, "mode: -l    crypt16\n");
#endif
#ifdef USE_BIGCRYPT
    fprintf(stderr, "mode: -b    bigcrypt\n");
#endif

    fprintf(stderr, "mode: -w    LanManager/NT\n");

	exit(-1);
}


main(int argc, char **argv, char **envp)
{
	char	salts[]=
	"abcdefghijklmnopqrstuvwxyz"
	"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
	"0123456789./";

	int		option;
	char	optstring[64]="vqc:s:h";
	char	salt[32];
	char	*saltp=NULL;
	int		quiet=0;
	int		compare=0;
	int		md5=0;
	int		c16=0;
	int		bc=0;
	int		w=0;
	char	*pass;
	char	*cpass;
	char	*(*my_crypt)(const char *key, const char *salt);

	struct timeval	tp;

#ifdef USE_SMB
	char *servicesf = CONFIGFILE;
#endif

#ifdef USE_MD5
	strcat(optstring,"m");
#endif
#ifdef USE_CRYPT16
	strcat(optstring,"l");
#endif
#ifdef USE_BIGCRYPT
	strcat(optstring,"b");
#endif

	strcat(optstring,"w");

	my_crypt=&crypt;

	while(( option = getopt(argc, argv, optstring))!=EOF )
	{
		switch(option)
		{
#ifdef USE_MD5
			case 'm':
				if ( c16|bc|w )
				{
					fprintf(stderr,"Can't use both md5 and other modes\n");
					exit(1);
				}
				md5=1;
#   if defined __linux__
#		ifdef USE_CRYPT_RN
				my_crypt=&my_cryptrn;
#		else
  				my_crypt=&crypt;
#		endif
#	else
				my_crypt=&__md5_crypt;
#	endif
				break;
#endif
#ifdef USE_CRYPT16
			case 'l':
				if ( md5|bc|w )
				{
					fprintf(stderr,"Can't use both crypt16 and other modes\n");
					exit(1);
				}
				c16=1;
				my_crypt=&crypt16;
				break;
#endif
#ifdef USE_BIGCRYPT
			case 'b':
				if ( md5|c16|w )
				{
					fprintf(stderr,"Can't use both bigcrypt and other modes\n");
					exit(1);
				}
				bc=1;
				my_crypt=&bigcrypt;
#				if ( defined __osf__ ) && ( ! defined MY_BIGCRYPT )
				set_auth_parameters(argc,argv);
#				endif
				break;
#endif
			case 'w':
#ifdef USE_SMB
				if ( md5|c16|bc )
				{
					fprintf(stderr,"Can't use both NT and other modes\n");
					exit(1);
				}
				w=1;
				my_crypt=&smb_crypt;
				TimeInit();
				charset_initialise();
				lp_load(servicesf,1,0,0);
				codepage_initialise(lp_client_code_page());
#else
				w=1;
				my_crypt=&smb_crypt;
#endif
				break;
			case 'q':
				quiet=1;
				break;
			case 'c':
				compare=1;
				cpass=optarg;
#ifdef USE_MD5
				if (cpass[0]=='$')
				{
					md5=1;
#	if defined __linux__
					my_crypt=&crypt;
#	else
					my_crypt=&__md5_crypt;
#	endif
				}
#endif
				break;
			case 's':
				saltp=optarg;
				break;
			case 'v':
				fprintf(stderr,"%s\n",VERS_STR);
				break;
			default:
				usage(argv[0]);
				break;
		}
	}
	if ( compare && saltp )
		usage(argv[0]);
				
	if (argv[optind])
	{
		if (compare)
		{
			pass=my_crypt(argv[optind],cpass);
			if ( !quiet ) puts (pass) ;
			return( strcmp ( cpass, pass ));
		}
		else
		{
			if ( saltp == NULL )
			{
				char	*c;
				int		i;
				saltp = salt;

#if defined __SVR4 || defined _HPUX_SOURCE || defined  __linux__ || defined __osf__ || defined __APPLE__

				gettimeofday(&tp, NULL);
#else
				gettimeofday(&tp);
#endif
				srand( (unsigned int) tp.tv_usec );

#ifndef USE_MD5
				salt[0] = *(salts+((long)rand() % (long)sizeof(salts)));
				salt[1] = *(salts+((long)rand() % (long)sizeof(salts)));
#else
				if ( md5 )
				{
					salt[0] = '$';
					salt[1] = '1';
					salt[2] = '$';
					c = &salt[3];

					for ( i=0; i<8; i++ )
					{
						*c++ = *(salts+((long)rand() % (long)sizeof(salts)));
					}
					*c++ = 0x00;
				}
				else
				{
					salt[0] = *(salts+((long)rand() % (long)sizeof(salts)));
					salt[1] = *(salts+((long)rand() % (long)sizeof(salts)));
				}
#endif
			}

			puts(my_crypt(argv[optind],saltp));
			return (0);

		}
	}
	else
	{
		return(1);
	}
}

#ifdef MY_BIGCRYPT
char *my_bigcrypt(const char *key, const char *salt)
{
#define BL 160 /* 2*AUTH_MAX_PASSWD_LENGTH */
	static char	c[BL];
	static char k[AUTH_MAX_PASSWD_LENGTH+1];
	char *p,*e;
	int	l;

	k[AUTH_MAX_PASSWD_LENGTH]=0x00;
	strncpy(k,key,AUTH_MAX_PASSWD_LENGTH);
	l=strlen(k);
	strncpy(c,salt,2);
	c[2]=0x00;
	for(p=k; p<k+l; p+=8)
	{
		salt=crypt(p,salt);
		salt+=2;
		strcat(c,salt);
	}
	return c;
}
#endif

#ifdef MY_CRYPT16
/* 
 * Ultrix crypt16 function, thanks to pcl@convex.oxford.ac.uk (Paul Leyland)
 *
 * libufc (recompile libufc with -DSTATIC="") required.
 */

extern char *output_conversion(ufc_long v1, ufc_long v2, char *salt);
extern ufc_long *_ufc_doit(
	ufc_long l1, ufc_long l2, ufc_long r1, ufc_long r2, ufc_long itr);

char *crypt16(const char *key, const char *salt)
{
	ufc_long *s, *t;
	char ktab[9], ttab[9];
	static char q[14], res[25];
	/*
	 * Hack DES tables according to salt
	 */
	setup_salt(salt);

	/*
	 * Setup key schedule
	 */
	clearmem(ktab, sizeof ktab);
	(void)strncpy(ktab, key, 8);
	ufc_mk_keytab(ktab);

	/*
	 * Go for first 20 DES encryptions
	 */
	s = _ufc_doit((ufc_long)0, (ufc_long)0, 
					(ufc_long)0, (ufc_long)0, (ufc_long)20);

	/*
	 * And convert back to 6 bit ASCII
	 */
	strcpy (res, output_conversion(s[0], s[1], salt));

	clearmem(ttab, sizeof ttab);
	if (strlen (key) > 8) (void)strncpy(ttab, key+8, 8);
	ufc_mk_keytab(ttab);

	/*
	 * Go for second 5 DES encryptions
	 */
	t = _ufc_doit((ufc_long)0, (ufc_long)0, 
					(ufc_long)0, (ufc_long)0, (ufc_long)5);
	/*
	 * And convert back to 6 bit ASCII
	 */
	strcpy (q, output_conversion(t[0], t[1], salt));
	strcpy (res+13, q+2);

	clearmem(ktab, sizeof ktab);
	(void)strncpy(ktab, key, 8);
	ufc_mk_keytab(ktab);

	return res;
}
#endif

#ifdef USE_SMB
char *smb_crypt(const char *key, const char *salt)
{
	/* salt is ignored */
	static unsigned char nt_p16[16], p16[16];
	static char nt_p32[33], p32[33];
	static char p[66];
	int	i;

	memset(nt_p16, '\0', 16);
	memset(nt_p32, '\0', 33);
	memset(p16, '\0', 16);
	memset(p32, '\0', 33);

	nt_lm_owf_gen(key, nt_p16, p16);
	for( i = 0; i < 16; i++)
	{
		slprintf(&nt_p32[i*2], 3, "%02X", nt_p16[i]);
		slprintf(&p32[i*2], 3, "%02X", p16[i]);
	}
	strcpy(p,p32);
	strcat(p,":");
	strcat(p,nt_p32);
	
	return p;
}
#endif

#ifdef USE_CRYPT_RN
char *my_cryptrn(const char *key, const char *salt)
{
	struct crypt_data state_data = {0};
	char *hash;
	hash=crypt_rn(key,salt,&state_data, sizeof(state_data));
	return hash;
}
#endif
