AD Password hash plugin for MSCHAPv2
807573Feb 1 2007 — edited Jan 4 2009I would like to share this plugin with all of you.This reduced the need for 3rd party wireless client software and will be leveraged by native desktop/laptop Microsoft clients such as Microsoft VPN and 802.11 wireless clients which require Microsoft (Active Directory-AD) for authentication. With this plugin, you can use Sun DS 5.2 server instead of AD. This may save AD licenses and also you don't have to buy 3rd party clients since we can use out of the box microsoft clients.
Following 4 files have source code, documentation, make file and LDIF file. This plugin is running in our servers from last 6 months only but never crashed or leaked memory so far :-). This was tested working for Microsoft VPN and/or 802.11x wireless clients using Steel Belted Radius server in the middle. If you guys want binary executable of this plugin for DS 5.2 Solaris or following 4 files transferred to you by FTP, HTTP or something, please send me that information to kusupudi@hotmail.com.
1. README
2. add_adpasshash_plugin.ldif
3. makefile_adpasshash
4. adpasshash.c
Thanks & Regrds
Krishna Kusupudi
$ cat README
INSTALLATION INSTRUCTIONS:
1. Add schema for adpwd attribute to all masters.
2. Update ACLs to assign same sensitivity level as userpassword to adpwd.
3. Copy plugin to master server as follows:
A. login to master Directory server as user server running as
B. cd <server-root>/lib/64
C. cp <your-dir>/adpasshash-plugin.so .
4. Add the configuration entry to the Master directory. For example:
ldapmodify -a -h <ldaphost> -D "cn=directory manager" -w <password> -f add_adpasshash_plugin.ldif
5. Restart the Directory server.
6. Repeat the above for each master server.
PLUGIN COMPILATION INSTRUCTIONS:
1. Install DS 5.2 first on any solaris server. example ldaphost
2. A. Login to Ldaphost
B. cd <server-root>/plugins/slapd/slapi/examples
C. cp <your-dir>/adpasshash.c .
D. cp <your-dir>/makefile_adpasshash .
E. make -f makefile_adpasshash
3. Now you have successfully created plugin .so file in Ldaphost under
/<server-root>/plugins/slapd/slapi/examples/64
Follow above Installation instructions to do Install
PLUGIN FUNCTIONALITY:
1. Purpose of this plugin is to support MSCHAP-v2 for Microsoft clients such as 802.11x and Microsoft VPN client which requires password to be in NT style MD4 hash.
2. This plugin intercepts password in clear text before it get hashed to SHA or SSHA into userpassword attribute and writes that password into another attribute with "MD4" hash or in "clear"
3. Take a look at add_adpasshash_plugin.ldif file. This plugin requires following 3 arguments:
nsslapd-pluginarg0: adpwd
nsslapd-pluginarg1: md4 [ or clear ]
nsslapd-pluginarg2: o=ad.com
arg0 is the name of the attribute you want to store password.
arg1 takes two values currently, either md4 or clear. Don't use clear for security reasons unless you really need to sync passwords from Meta directory
arg2 takes name of the suffix you want to run plugin against, set it to o=example.com. We added this argument because we don't want plugin to apply to "cn=changelog" or "o=netscaperoot" suffixes.
4. Plugin takes no action, if userpassword value coming across the wire is already hashed, such as if it begins with "{SHA}" or "{SSHA}"
TEST RESULTS:
A. 1000 password updates took 100 seconds with out plugin and 114 seconds with plugin. This is due to fact that this is a post-op plugin where each password update generates additional mod. I am thinking about converting this to pre-op when I have a free time.
B. Never crashed slapd, Running from last 6 months with no slapd crash or memory leaks so far.
------------------------------------------------------------------------------- -----------------------------
$ cat add_adpasshash_plugin.ldif
dn: cn=AD Passhash,cn=plugins,cn=config
objectClass: top
objectClass: nsSlapdPlugin
objectClass: extensibleObject
cn: AD Passhash
nsslapd-pluginPath: /opt/sunone/server5/lib/adpasshash-plugin.so
nsslapd-pluginInitfunc: postop_adpasshash_init
nsslapd-pluginType: postoperation
nsslapd-pluginEnabled: on
nsslapd-plugin-depends-on-type: database
nsslapd-pluginId: postop-adpasshash
nsslapd-pluginVersion: 1.0
nsslapd-pluginVendor: Krishna Kusupudi (kusupudi@hotmail.com)
nsslapd-pluginDescription: plug-in for hashing password with given hash type
nsslapd-pluginarg0: adpwd
nsslapd-pluginarg1: md4
nsslapd-pluginarg2: o=example.com
------------------------------------------------------------------------------- --------------------------
$ cat makefile_adpasshash
#
# Copyright 2002 Sun Microsystems, Inc. All Rights Reserved
# Use of this product is subject to license terms.
#
# SOLARIS Makefile for 64-bit Directory Server plug-in examples
#
CC = cc
LD = ld
INCLUDE_FLAGS = -I../include
#CFLAGS = $(INCLUDE_FLAGS) -D_REENTRANT -KPIC -xarch=v9 -DUSE_64
CFLAGS = $(INCLUDE_FLAGS) -fPIC -m64
LDFLAGS = -G
#LDFLAGS = -shared
DIR64 = 64
OBJS = adpasshash.o
all: MKDIR64 $(DIR64)/adpasshash-plugin.so
MKDIR64:
@if [ ! -d $(DIR64) ]; then mkdir $(DIR64); fi
$(DIR64)/adpasshash-plugin.so: $(OBJS)
$(LD) $(LDFLAGS) -o $@ $(OBJS)
.c.o:
$(CC) $(CFLAGS) -c $<
clean:
-rm -f $(OBJS) adpasshash-plugin.so $(DIR64)/adpasshash-plugin.so
------------------------------------------------------------------------------- ----------------------
$ cat adpasshash.c
/*
* Portions Copyright 2003 Sun Microsystems, Inc. All Rights Reserved
* Use is subject to license terms.
* Some preexisting portions Copyright 1997-2001 Netscape Communications Corp.
* All rights reserved.
* Rest of the code by krishna Kusupudi (kusupudi@hotmail.com)
* Created on: 02/15/2006, krishna Kusupudi
* Last modified: 09/20/2006
*/
/* ***************************************************************
Unix SMB/Netbios implementation.
Version 1.9.
a implementation of MD4 designed for use in the SMB authentication protocol
Copyright (C) Andrew Tridgell 1997
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/************************************************************
adpasshash.c
This source file provides post-operation plug-in for capturing
password in clear text before it get hashed into "userPassword
attribute. It then copies that password to attribute you specify
as an argument to plugin in either md4 hash or in cleat text.
* postop_adpasshash_add (called after an LDAP add operation)
* postop_adpasshash_mod (called after an LDAP modify operation)
ACTIVATING THIS PLUG-IN
-----------------------
For more detailed information about this plugin, please read README file.
*************************************************************/
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#ifndef _WIN32
#include <libgen.h>
#endif
#include "slapi-plugin.h"
Slapi_PluginDesc postop_desc = {
"postop-adpasshash", /* plug-in identifier */
"krishna Kusupudi.", /* vendor name */
"1.0", /* plug-in revision number */
"post-operation plug-in for generating hash password" /* plug-in description */
};
static Slapi_ComponentId * postop_id; /* Used to set log, also called plugin_id */
typedef unsigned long int uint32;
static uint32 A, B, C, D;
static uint32 F(uint32 X, uint32 Y, uint32 Z)
{
return (X&Y) | ((~X)&Z);
}
static uint32 G(uint32 X, uint32 Y, uint32 Z)
{
return (X&Y) | (X&Z) | (Y&Z);
}
static uint32 H(uint32 X, uint32 Y, uint32 Z)
{
return X^Y^Z;
}
static uint32 lshift(uint32 x, int s)
{
x &= 0xFFFFFFFF;
return ((x<<s)&0xFFFFFFFF) | (x>>(32-s));
}
#define ROUND1(a,b,c,d,k,s) a = lshift(a + F(b,c,d) + X[k], s)
#define ROUND2(a,b,c,d,k,s) a = lshift(a + G(b,c,d) + X[k] + (uint32)0x5A827999,s)
#define ROUND3(a,b,c,d,k,s) a = lshift(a + H(b,c,d) + X[k] + (uint32)0x6ED9EBA1,s)
static void mdfour64(uint32 *M);
static void copy64(uint32 *M, unsigned char *in);
static void copy4(unsigned char *out,uint32 x);
int PutUniCode(char *dst,char *src);
void mdfour(unsigned char *out, unsigned char *in, int n);
int md4_hexa_hash(unsigned char *in, unsigned char *out);
int modify_secondpw(char *newpw_attrname, char *newpw, char *dn);
int issuffix_match(char * user_DN, char * plugin_suffix);
/* Capture clear text password for mod operation. */
int postop_adpasshash_mod(Slapi_PBlock * pb)
{
char * dn; /* DN of entry to modify */
LDAPMod ** mods; /* Modifications to apply */
int is_repl = 0; /* Is this replication? */
int connId, opId, rc = 0, rcode = 0;
long msgId;
int iterator;
char * newpw = NULL;
char * newpw_md4 = NULL;
char ** argv; /* Args from configuration */
int argc; /* entry for plug-in. */
rc |= slapi_pblock_get(pb, SLAPI_MODIFY_TARGET, &dn);
rc |= slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods);
rc |= slapi_pblock_get(pb, SLAPI_OPERATION_MSGID, &msgId);
rc |= slapi_pblock_get(pb, SLAPI_CONN_ID, &connId);
rc |= slapi_pblock_get(pb, SLAPI_OPERATION_ID, &opId);
rc |= slapi_pblock_get(pb, SLAPI_PLUGIN_ARGV, &argv);
rc |= slapi_pblock_get(pb, SLAPI_PLUGIN_ARGC, &argc);
rc |= slapi_pblock_get(pb, SLAPI_RESULT_CODE, &rcode);
rc |= slapi_pblock_get(pb, SLAPI_IS_REPLICATED_OPERATION, &is_repl);
/* if this is replicated event OR SLAPI_RESULT_CODE is not 0, ignore it */
if ((is_repl) || (rcode != 0) || (rc != 0)) {
return (rc);
}
/* Call this custom function to comapre user's suffix with plugins suffix */
if (issuffix_match(dn,argv[2]) != 0) {
return (0);
}
/* Cycle through the modifies and see if we have a password change */
for (iterator=0; mods[iterator] != NULL; iterator++)
{
if (strcasecmp(mods[iterator]->mod_type,"unhashed#user#password") == 0)
{
newpw = slapi_ch_malloc(strlen(mods[iterator]->mod_bvalues[0]->bv_val)* sizeof(char *));
newpw = slapi_ch_strdup(mods[iterator]->mod_bvalues[0]->bv_val);
/* Do nothing if the password is already hashed to SHA or SSHA */
if ((newpw == NULL) || (strncasecmp (newpw,"{SHA}",5) == 0) || (strncasecmp (newpw,"{SSHA}",6) == 0)) {
slapi_ch_free((void **)&newpw);
return (0);
}
if ( strcasecmp(argv[1],"clear") == 0 )
{
/* Call modify function to update second password attribute in clear */
rc = modify_secondpw(argv[0], newpw, dn);
}
else if ( strcasecmp(argv[1],"md4") == 0 )
{
newpw_md4 = slapi_ch_malloc(40); /* grab space for md4 hash */
/* call function md4_hexa_hash to get hash */
rc = md4_hexa_hash(newpw, newpw_md4);
if (rc == 0)
{
/* Call modify function to update second password attribute */
rc = modify_secondpw(argv[0], newpw_md4, dn);
}
/* Free the memory allocated by slapi_ch_malloc */
slapi_ch_free((void **) &newpw_md4);
} /* End of if ( strcasecmp(argv[1] */
slapi_ch_free((void **)&newpw);
} /* End of if (strcasecmp(mods[iterator] */
} /* End of for (iterator=0; */
return (0);
} /* End of postop_adpasshash_mod(Slapi_PBlock * pb) */
/* Capture clear text password for add operation. */
int postop_adpasshash_add(Slapi_PBlock * pb)
{
char * dn; /* DN of entry to modify */
Slapi_Entry * entry; /* Entry to add */
Slapi_Attr * attribute; /* Entry attributes */
Slapi_Value * value; /* Attribute values */
int is_repl = 0; /* Is this replication? */
int connId, opId, rc = 0, rcode = 0;
long msgId;
const char * string; /* Holder for current password. */
char * newpw = NULL;
char * newpw_md4 = NULL;
char ** argv; /* Args from configuration */
int argc; /* entry for plug-in. */
rc |= slapi_pblock_get(pb, SLAPI_ADD_TARGET, &dn);
rc |= slapi_pblock_get(pb, SLAPI_ADD_ENTRY, &entry);
rc |= slapi_pblock_get(pb, SLAPI_OPERATION_MSGID, &msgId);
rc |= slapi_pblock_get(pb, SLAPI_CONN_ID, &connId);
rc |= slapi_pblock_get(pb, SLAPI_OPERATION_ID, &opId);
rc |= slapi_pblock_get(pb, SLAPI_PLUGIN_ARGV, &argv);
rc |= slapi_pblock_get(pb, SLAPI_PLUGIN_ARGC, &argc);
rc |= slapi_pblock_get(pb, SLAPI_RESULT_CODE, &rcode);
rc |= slapi_pblock_get(pb, SLAPI_IS_REPLICATED_OPERATION, &is_repl);
/* if this is replicated event OR SLAPI_RESULT_CODE is not 0, ignore it */
if ((is_repl) || (rcode != 0) || (rc != 0)) {
return (rc);
}
/* Call this custom function to comapre user's suffix with plugins suffix */
if (issuffix_match(dn,argv[2]) != 0) {
return (0);
}
/* Check in the entry to see if it has password in it */
newpw = slapi_entry_attr_get_charptr(entry, "unhashed#user#password");
/* Do nothing if the password is already hashed to SHA or SSHA or is null */
if ((newpw == NULL) || (strncasecmp (newpw,"{SHA}",5) == 0) || (strncasecmp (newpw,"{SSHA}",6) == 0)) {
slapi_ch_free((void **)&newpw);
return (0);
}
if ( strcasecmp(argv[1],"clear") == 0 ) {
/* Call modify function to update second password attribute in clear */
rc = modify_secondpw(argv[0], newpw, dn);
}
else if ( strcasecmp(argv[1],"md4") == 0 ) {
newpw_md4 = slapi_ch_malloc(40); /* grab space for md4 hash */
/* call function md4_hexa_hash to get hash */
rc = md4_hexa_hash(newpw, newpw_md4);
if (rc == 0) {
/* Call modify function to update second password attribute */
rc = modify_secondpw(argv[0], newpw_md4, dn);
}
/* Free the memory allocated by slapi_ch_malloc */
slapi_ch_free((void **) &newpw_md4);
} /* End of if strcasecmp(argv[1] */
slapi_ch_free((void **)&newpw);
return (0);
} /* End of postop_adpasshash_add(Slapi_PBlock * pb) */
/* Register the plug-in with the server. */
#ifdef _WIN32
__declspec(dllexport)
#endif
int
postop_adpasshash_init(Slapi_PBlock * pb)
{
int rc = 0; /* 0 means success */
int i;
char ** argv; /* Args from configuration */
int argc; /* entry for plug-in. */
rc |= slapi_pblock_get(pb, SLAPI_PLUGIN_ARGV, &argv);
rc |= slapi_pblock_get(pb, SLAPI_PLUGIN_ARGC, &argc);
if (rc != 0 || argc !=3 || argv[0] == NULL || argv[1] == NULL || argv[2] == NULL) {
slapi_log_error_ex(
-1, /* errorId */
SLAPI_LOG_NO_MSGID,
SLAPI_LOG_NO_CONNID,
SLAPI_LOG_NO_OPID,
"postop_adpasshash_init",
"postop_adpasshash plug-in",
"Failed to get plugin arguments\n",
NULL
);
return (-1);
} /* End of if (rc != 0 */
rc |= slapi_pblock_set( /* Plug-in API version */
pb,
SLAPI_PLUGIN_VERSION,
SLAPI_PLUGIN_CURRENT_VERSION
);
rc |= slapi_pblock_set( /* Plug-in description */
pb,
SLAPI_PLUGIN_DESCRIPTION,
(void *) &postop_desc
);
rc |= slapi_pblock_set( /* Post-op add function */
pb,
SLAPI_PLUGIN_POST_ADD_FN,
(void *) postop_adpasshash_add
);
rc |= slapi_pblock_set( /* Post-op modify function */
pb,
SLAPI_PLUGIN_POST_MODIFY_FN,
(void *) postop_adpasshash_mod
);
/* Plug-in identifier is required for internal search. */
rc |= slapi_pblock_get(pb, SLAPI_PLUGIN_IDENTITY, &postop_id);
return (rc);
}
/* THis function checks if the suffix of an entry is the one provided in ARGV[2] */
int issuffix_match (char * user_DN, char * plugin_suffix)
{
Slapi_DN * comp_user_DN; /* Copy of user's DN that we normalize */
Slapi_DN * comp_plugin_suffix; /* suffix of user's entry */
comp_user_DN = slapi_sdn_new();
comp_plugin_suffix = slapi_sdn_new();
slapi_sdn_init_dn_byref(comp_user_DN,user_DN);
slapi_sdn_init_dn_byref(comp_plugin_suffix,plugin_suffix);
if (slapi_sdn_issuffix(comp_user_DN,comp_plugin_suffix) == 0)
{
slapi_sdn_free(&comp_user_DN);
slapi_sdn_free(&comp_plugin_suffix);
return (-1);
}
slapi_sdn_free(&comp_user_DN);
slapi_sdn_free(&comp_plugin_suffix);
return (0);
} /* End of int issuffix_match */
/* This function generates NT hash md4 in hexa format */
int md4_hexa_hash(unsigned char *in, unsigned char *out)
{
char *hold = NULL;
#define NTPASSWDLEN 128
char * prefix = "{md4}";
char passwd[NTPASSWDLEN+1];
int i,uni_len;
unsigned char *p16 = NULL;
char outbuffer[33];
unsigned char c;
hold = slapi_ch_malloc(NTPASSWDLEN * 2); /* grab space for unicode */
if (hold == NULL) return NULL;
strncpy(passwd,in,NTPASSWDLEN);
passwd[NTPASSWDLEN]='\0';
uni_len = PutUniCode(hold, passwd); /* convert to unicode and
return correct unicode length for md4 */
p16 = slapi_ch_malloc(17); /* grab space for md4 hash */
if (p16 == NULL) return NULL;
memset(p16,'\0',17);
mdfour(p16,hold, uni_len);
/* Convert hash from binary to hexa */
for(i=0;i<16;i++) {
c=p16;
sprintf(outbuffer+2*i,"%x",(c>>4) & 0x0f);
sprintf(outbuffer+2*i+1,"%x",c & 0x0f);
}
/* convert to uppercase */
for(i=0;i<32;i++)
outbuffer = toupper(outbuffer);
outbuffer[32]='\0';
/* Prefix {md4} to the hexa string to make it work with Steel Belted Radius */
sprintf (out, "%s%s", prefix, outbuffer);
/* Free the memory allocated by slapi_ch_malloc */
slapi_ch_free((void **) &hold);
slapi_ch_free((void **) &p16);
slapi_ch_free_string(&prefix);
return (0);
} /* End of md4_hexa_hash function */
/* This function adds second password attribute to server */
int modify_secondpw(char *newpw_attrname, char *newpw, char *dn)
{
Slapi_Entry * entry; /* Entry holder for internal ops */
Slapi_PBlock * pbin; /* PBlock for internal ops */
LDAPMod mod_attr; /* Attribute to modify */
LDAPMod * modsare[2]; /* Array of modifications */
char * newpw_vals[] = { newpw, NULL}; /* second password attr value */
int rc = 0;
mod_attr.mod_type = newpw_attrname;
mod_attr.mod_op = LDAP_MOD_REPLACE;
mod_attr.mod_values = newpw_vals;
modsare[0] = &mod_attr;
modsare[1] = NULL;
pbin = slapi_pblock_new(); /* Set up a PBlock... */
rc = slapi_modify_internal_set_pb(
pbin,
dn,
modsare,
NULL, /* No controls */
NULL, /* DN rather than unique ID */
postop_id,
SLAPI_OP_FLAG_NEVER_CHAIN /* Never chain this operation. */
);
if (rc != 0) {
slapi_pblock_destroy(pbin);
return (-1);
}
rc = slapi_modify_internal_pb(pbin); /* Unlike internal add, */
/* nothing consumed here */
/* ... get status ... */
slapi_pblock_get(pbin, SLAPI_PLUGIN_INTOP_RESULT, &rc);
if (rc != LDAP_SUCCESS) {
slapi_pblock_destroy(pbin);
return (-1);
}
slapi_pblock_destroy(pbin); /* ... clean up the PBlock. */
return (0);
} /* End of modify_secondpw function */
/*******************************************************************
write a string in unicoode format
********************************************************************/
int PutUniCode(char *dst,char *src)
{
int ret = 0;
while (*src) {
dst[ret++] = src[0];
dst[ret++] = 0;
src++;
}
dst[ret++]=0;
dst[ret++]=0;
return(ret-2); /* the way they do the md4 hash they don't represent
the last null. ie 'A' becomes just 0x41 0x00 - not
0x41 0x00 0x00 0x00 */
}
/* End of function PutUniCode */
/* this applies md4 to 64 byte chunks */
static void mdfour64(uint32 *M)
{
int j;
uint32 AA, BB, CC, DD;
uint32 X[16];
for (j=0;j<16;j++)
X[j] = M[j];
AA = A; BB = B; CC = C; DD = D;
ROUND1(A,B,C,D, 0, 3); ROUND1(D,A,B,C, 1, 7);
ROUND1(C,D,A,B, 2, 11); ROUND1(B,C,D,A, 3, 19);
ROUND1(A,B,C,D, 4, 3); ROUND1(D,A,B,C, 5, 7);
ROUND1(C,D,A,B, 6, 11); ROUND1(B,C,D,A, 7, 19);
ROUND1(A,B,C,D, 8, 3); ROUND1(D,A,B,C, 9, 7);
ROUND1(C,D,A,B, 10, 11); ROUND1(B,C,D,A, 11, 19);
ROUND1(A,B,C,D, 12, 3); ROUND1(D,A,B,C, 13, 7);
ROUND1(C,D,A,B, 14, 11); ROUND1(B,C,D,A, 15, 19);
ROUND2(A,B,C,D, 0, 3); ROUND2(D,A,B,C, 4, 5);
ROUND2(C,D,A,B, 8, 9); ROUND2(B,C,D,A, 12, 13);
ROUND2(A,B,C,D, 1, 3); ROUND2(D,A,B,C, 5, 5);
ROUND2(C,D,A,B, 9, 9); ROUND2(B,C,D,A, 13, 13);
ROUND2(A,B,C,D, 2, 3); ROUND2(D,A,B,C, 6, 5);
ROUND2(C,D,A,B, 10, 9); ROUND2(B,C,D,A, 14, 13);
ROUND2(A,B,C,D, 3, 3); ROUND2(D,A,B,C, 7, 5);
ROUND2(C,D,A,B, 11, 9); ROUND2(B,C,D,A, 15, 13);
ROUND3(A,B,C,D, 0, 3); ROUND3(D,A,B,C, 8, 9);
ROUND3(C,D,A,B, 4, 11); ROUND3(B,C,D,A, 12, 15);
ROUND3(A,B,C,D, 2, 3); ROUND3(D,A,B,C, 10, 9);
ROUND3(C,D,A,B, 6, 11); ROUND3(B,C,D,A, 14, 15);
ROUND3(A,B,C,D, 1, 3); ROUND3(D,A,B,C, 9, 9);
ROUND3(C,D,A,B, 5, 11); ROUND3(B,C,D,A, 13, 15);
ROUND3(A,B,C,D, 3, 3); ROUND3(D,A,B,C, 11, 9);
ROUND3(C,D,A,B, 7, 11); ROUND3(B,C,D,A, 15, 15);
A += AA; B += BB; C += CC; D += DD;
A &= 0xFFFFFFFF; B &= 0xFFFFFFFF;
C &= 0xFFFFFFFF; D &= 0xFFFFFFFF;
for (j=0;j<16;j++)
X[j] = 0;
}
static void copy64(uint32 *M, unsigned char *in)
{
int i;
for (i=0;i<16;i++)
M = (in[i*4+3]<<24) | (in[i*4+2]<<16) |
(in[i*4+1]<<8) | (in[i*4+0]<<0);
}
static void copy4(unsigned char *out,uint32 x)
{
out[0] = x&0xFF;
out[1] = (x>>8)&0xFF;
out[2] = (x>>16)&0xFF;
out[3] = (x>>24)&0xFF;
}
/* produce a md4 message digest from data of length n bytes */
void mdfour(unsigned char *out, unsigned char *in, int n)
{
unsigned char buf[128];
uint32 M[16];
uint32 b = n * 8;
int i;
A = 0x67452301;
B = 0xefcdab89;
C = 0x98badcfe;
D = 0x10325476;
while (n > 64) {
copy64(M, in);
mdfour64(M);
in += 64;
n -= 64;
}
for (i=0;i<128;i++)
buf = 0;
memcpy(buf, in, n);
buf[n] = 0x80;
if (n <= 55) {
copy4(buf+56, b);
copy64(M, buf);
mdfour64(M);
} else {
copy4(buf+120, b);
copy64(M, buf);
mdfour64(M);
copy64(M, buf+64);
mdfour64(M);
}
for (i=0;i<128;i++)
buf = 0;
copy64(M, buf);
copy4(out, A);
copy4(out+4, B);
copy4(out+8, C);
copy4(out+12, D);
A = B = C = D = 0;
}