diff options
author | Guy Harris <guy@alum.mit.edu> | 2002-12-03 00:37:27 +0000 |
---|---|---|
committer | Guy Harris <guy@alum.mit.edu> | 2002-12-03 00:37:27 +0000 |
commit | dd8f630768f5c83c677542759ff26712c43bff39 (patch) | |
tree | d58e4d1eb2eb54e8df018650a826497314ac953d | |
parent | 59932f27227c4769be94fb46936d123d1770c1a2 (diff) | |
download | wireshark-dd8f630768f5c83c677542759ff26712c43bff39.tar.gz |
From Devin Heitmueller:
add MD4 and RC4 crypto support;
use it to decrypt the NT password encryption block in
UnicodeChangePassword2.
svn path=/trunk/; revision=6727
-rw-r--r-- | AUTHORS | 3 | ||||
-rw-r--r-- | Makefile.am | 6 | ||||
-rw-r--r-- | Makefile.nmake | 4 | ||||
-rw-r--r-- | crypt-md4.c | 174 | ||||
-rw-r--r-- | crypt-md4.h | 23 | ||||
-rw-r--r-- | crypt-rc4.c | 81 | ||||
-rw-r--r-- | crypt-rc4.h | 26 | ||||
-rw-r--r-- | packet-dcerpc-samr.c | 176 |
8 files changed, 479 insertions, 14 deletions
@@ -1228,6 +1228,9 @@ Devin Heitmueller <dheitmueller[AT]netilla.com> { being present in the packet Dissection of NTLMSSP authentication for DCERPC Show proper field names for SAMR UnicodeChangePassword2 + Add MD4 and RC4 crypto support + Decrypt the NT password encryption block in + UnicodeChangePassword2 } Chenjiang Hu <chu[AT]chiaro.com> { diff --git a/Makefile.am b/Makefile.am index 3c90558bfa..b696a40d2b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,7 +1,7 @@ # Makefile.am # Automake file for Ethereal # -# $Id: Makefile.am,v 1.521 2002/11/15 22:45:52 sahlberg Exp $ +# $Id: Makefile.am,v 1.522 2002/12/03 00:37:26 guy Exp $ # # Ethereal - Network traffic analyzer # By Gerald Combs <gerald@ethereal.com> @@ -610,6 +610,10 @@ ETHEREAL_COMMON_SRC = \ column.h \ conditions.c \ conditions.h \ + crypt-md4.c \ + crypt-md4.h \ + crypt-rc4.c \ + crypt-rc4.h \ etypes.h \ file.h \ follow.c \ diff --git a/Makefile.nmake b/Makefile.nmake index 7fd0a10798..68e738a897 100644 --- a/Makefile.nmake +++ b/Makefile.nmake @@ -1,7 +1,7 @@ ## Makefile for building ethereal.exe with Microsoft C and nmake ## Use: $(MAKE) /$(MAKEFLAGS) -f makefile.nmake # -# $Id: Makefile.nmake,v 1.258 2002/11/15 22:45:52 sahlberg Exp $ +# $Id: Makefile.nmake,v 1.259 2002/12/03 00:37:27 guy Exp $ include config.nmake include <win32.mak> @@ -344,6 +344,8 @@ ETHEREAL_COMMON_OBJECTS = \ cfile.obj \ column.obj \ conditions.obj \ + crypt-md4.obj \ + crypt-rc4.obj \ follow.obj \ getopt.obj \ in_cksum.obj \ diff --git a/crypt-md4.c b/crypt-md4.c new file mode 100644 index 0000000000..5ec24b8c75 --- /dev/null +++ b/crypt-md4.c @@ -0,0 +1,174 @@ +/* + Unix SMB/CIFS implementation. + a implementation of MD4 designed for use in the SMB authentication protocol + Copyright (C) Andrew Tridgell 1997-1998. + + $Id: crypt-md4.c,v 1.1 2002/12/03 00:37:27 guy Exp $ + + 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. +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include <glib.h> + +/* NOTE: This code makes no attempt to be fast! + + It assumes that a int is at least 32 bits long +*/ + +static guint32 A, B, C, D; + +static guint32 F(guint32 X, guint32 Y, guint32 Z) +{ + return (X&Y) | ((~X)&Z); +} + +static guint32 G(guint32 X, guint32 Y, guint32 Z) +{ + return (X&Y) | (X&Z) | (Y&Z); +} + +static guint32 H(guint32 X, guint32 Y, guint32 Z) +{ + return X^Y^Z; +} + +static guint32 lshift(guint32 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] + (guint32)0x5A827999,s) +#define ROUND3(a,b,c,d,k,s) a = lshift(a + H(b,c,d) + X[k] + (guint32)0x6ED9EBA1,s) + +/* this applies md4 to 64 byte chunks */ +static void mdfour64(guint32 *M) +{ + int j; + guint32 AA, BB, CC, DD; + guint32 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(guint32 *M, const unsigned char *in) +{ + int i; + + for (i=0;i<16;i++) + M[i] = (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, guint32 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 crypt_md4(unsigned char *out, const unsigned char *in, int n) +{ + unsigned char buf[128]; + guint32 M[16]; + guint32 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[i] = 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[i] = 0; + copy64(M, buf); + + copy4(out, A); + copy4(out+4, B); + copy4(out+8, C); + copy4(out+12, D); + + A = B = C = D = 0; +} + + diff --git a/crypt-md4.h b/crypt-md4.h new file mode 100644 index 0000000000..2fd554d29e --- /dev/null +++ b/crypt-md4.h @@ -0,0 +1,23 @@ +/* + Unix SMB/CIFS implementation. + a implementation of MD4 designed for use in the SMB authentication protocol + Copyright (C) Andrew Tridgell 1997-1998. + + $Id: crypt-md4.h,v 1.1 2002/12/03 00:37:27 guy Exp $ + + 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. +*/ + +void crypt_md4(unsigned char *out, const unsigned char *in, int n); diff --git a/crypt-rc4.c b/crypt-rc4.c new file mode 100644 index 0000000000..73d08d45eb --- /dev/null +++ b/crypt-rc4.c @@ -0,0 +1,81 @@ +/* + Unix SMB/CIFS implementation. + + a partial implementation of RC4 designed for use in the + SMB authentication protocol + + Copyright (C) Andrew Tridgell 1998 + + $Id: crypt-rc4.c,v 1.1 2002/12/03 00:37:27 guy Exp $ + + 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. +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include <glib.h> + +/* Perform RC4 on a block of data using specified key. "data" is a pointer + to the block to be processed. Output is written to same memory as input, + so caller may need to make a copy before calling this function, since + the input will be overwritten. "val" specifies length of data buffer. + "key" is assumed to be a 16 octets in length + + Taken from Samba source code. In the long term, it might be nice to have + the input and output buffer differ, have a length specifier for the key, + and separate the initialization function from the process function (as is + done with the Alleged-RC4 implementation). +*/ + +void crypt_rc4( unsigned char *data, const unsigned char *key, int val) +{ + unsigned char s_box[256]; + unsigned char index_i = 0; + unsigned char index_j = 0; + unsigned char j = 0; + int ind; + + for (ind = 0; ind < 256; ind++) + { + s_box[ind] = (unsigned char)ind; + } + + for( ind = 0; ind < 256; ind++) + { + unsigned char tc; + + j += (s_box[ind] + key[ind%16]); + + tc = s_box[ind]; + s_box[ind] = s_box[j]; + s_box[j] = tc; + } + for( ind = 0; ind < val; ind++) + { + unsigned char tc; + unsigned char t; + + index_i++; + index_j += s_box[index_i]; + + tc = s_box[index_i]; + s_box[index_i] = s_box[index_j]; + s_box[index_j] = tc; + + t = s_box[index_i] + s_box[index_j]; + data[ind] = data[ind] ^ s_box[t]; + } +} diff --git a/crypt-rc4.h b/crypt-rc4.h new file mode 100644 index 0000000000..ec3dffd692 --- /dev/null +++ b/crypt-rc4.h @@ -0,0 +1,26 @@ +/* + Unix SMB/CIFS implementation. + + a partial implementation of RC4 designed for use in the + SMB authentication protocol + + Copyright (C) Andrew Tridgell 1998 + + $Id: crypt-rc4.h,v 1.1 2002/12/03 00:37:27 guy Exp $ + + 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. +*/ + +void crypt_rc4( unsigned char *data, const unsigned char *key, int val); diff --git a/packet-dcerpc-samr.c b/packet-dcerpc-samr.c index 792cfda406..f9d0f53d2b 100644 --- a/packet-dcerpc-samr.c +++ b/packet-dcerpc-samr.c @@ -3,7 +3,7 @@ * Copyright 2001, Tim Potter <tpot@samba.org> * 2002 Added all command dissectors Ronnie Sahlberg * - * $Id: packet-dcerpc-samr.c,v 1.60 2002/11/10 20:17:52 guy Exp $ + * $Id: packet-dcerpc-samr.c,v 1.61 2002/12/03 00:37:27 guy Exp $ * * Ethereal - Network traffic analyzer * By Gerald Combs <gerald@ethereal.com> @@ -31,12 +31,15 @@ #include <glib.h> #include <epan/packet.h> #include <string.h> +#include "prefs.h" #include "packet-dcerpc.h" #include "packet-dcerpc-nt.h" #include "packet-dcerpc-samr.h" #include "packet-dcerpc-lsa.h" #include "smb.h" /* for "NT_errors[]" */ #include "packet-smb-common.h" +#include "crypt-md4.h" +#include "crypt-rc4.h" #ifdef NEED_SNPRINTF_H # include "snprintf.h" @@ -83,6 +86,10 @@ static int hf_samr_crypt_hash = -1; static int hf_samr_lm_change = -1; static int hf_samr_lm_passchange_block = -1; static int hf_samr_nt_passchange_block = -1; +static int hf_samr_nt_passchange_block_decrypted = -1; +static int hf_samr_nt_passchange_block_newpass = -1; +static int hf_samr_nt_passchange_block_newpass_len = -1; +static int hf_samr_nt_passchange_block_pseudorandom = -1; static int hf_samr_lm_verifier = -1; static int hf_samr_nt_verifier = -1; static int hf_samr_attrib = -1; @@ -203,6 +210,9 @@ static e_uuid_t uuid_dcerpc_samr = { static guint16 ver_dcerpc_samr = 1; +/* Configuration variables */ +static char *nt_password = NULL; + /* Dissect connect specific access rights */ static gint hf_access_connect_unknown_01 = -1; @@ -1637,16 +1647,93 @@ samr_dissect_CRYPT_HASH(tvbuff_t *tvb, int offset, return offset; } +#define NT_BLOCK_SIZE 516 + +static int +samr_dissect_decrypted_NT_PASSCHANGE_BLOCK(tvbuff_t *tvb, int offset, + packet_info *pinfo _U_, proto_tree *tree, + char *drep _U_) +{ + guint32 new_password_len = 0; + guint32 pseudorandom_len = 0; + const char *printable_password; + guint16 bc; + int result_length; + + /* The length of the new password is represented in the last four + octets of the decrypted buffer. Since the password length cannot + exceed 512, we can check the contents of those bytes to determine + if decryption was successful. If the decrypted contents of those + four bytes is less than 512, then there is a 99% chance that + we decrypted the buffer successfully. Of course, this isn't good + enough for a security application, (NT uses the "verifier" field + to come to the same conclusion), but it should be good enough for + our dissector. */ + + new_password_len = tvb_get_letohl(tvb, 512); + + if (new_password_len <= 512) + { + /* Decryption successful */ + proto_tree_add_text (tree, tvb, offset, -1, + "Decryption of NT Password Encrypted block successful"); + + /* Whatever is before the password is pseudorandom data. We calculate + the length by examining the password length (at the end), and working + backward */ + pseudorandom_len = NT_BLOCK_SIZE - new_password_len - 4; + + /* Pseudorandom data padding up to password */ + proto_tree_add_item(tree, hf_samr_nt_passchange_block_pseudorandom, + tvb, offset, pseudorandom_len, FALSE); + offset += pseudorandom_len; + + /* The new password itself */ + bc = new_password_len; + printable_password = get_unicode_or_ascii_string(tvb, &offset, + TRUE, + &result_length, + FALSE, TRUE, &bc); + proto_tree_add_string(tree, hf_samr_nt_passchange_block_newpass, + tvb, offset, result_length, + printable_password); + offset += new_password_len; + + /* Length of password */ + proto_tree_add_item(tree, hf_samr_nt_passchange_block_newpass_len, + tvb, offset, 4, FALSE); + } + else + { + /* Decryption failure. Just show the encrypted block */ + proto_tree_add_text (tree, tvb, offset, -1, + "Decryption of NT Passchange block failed"); + + proto_tree_add_item(tree, hf_samr_nt_passchange_block_decrypted, tvb, + offset, NT_BLOCK_SIZE, FALSE); + } + + return NT_BLOCK_SIZE; +} + static int samr_dissect_NT_PASSCHANGE_BLOCK(tvbuff_t *tvb, int offset, packet_info *pinfo _U_, proto_tree *tree, char *drep _U_) { dcerpc_info *di; - - /* Right now, this just dumps the output. In the long term, we can use - the algorithm discussed in lkcl -"DCE/RPC over SMB" page 257 to - actually decrypt the block */ + unsigned char password_unicode[256]; + unsigned char password_md4_hash[16]; + guint8 *block; + tvbuff_t *decr_tvb = NULL; /* Used to store decrypted buffer */ + int password_len_unicode = 0; + guint32 i; + + memset(password_unicode, 0, sizeof(password_unicode)); + + /* This implements the the algorithm discussed in lkcl -"DCE/RPC + over SMB" page 257. Note that this code does not properly support + Unicode. */ di=pinfo->private_data; if(di->conformant_run){ @@ -1654,9 +1741,47 @@ samr_dissect_NT_PASSCHANGE_BLOCK(tvbuff_t *tvb, int offset, return offset; } - proto_tree_add_item(tree, hf_samr_nt_passchange_block, tvb, offset, - 516, FALSE); - offset += 516; + if ((block = g_malloc(NT_BLOCK_SIZE)) == NULL) + { + /* Could not allocate memory, just skip the field entirely */ + offset += NT_BLOCK_SIZE; + return offset; + } + + /* Convert the password provided in the Ethereal GUI to Unicode + (UCS-2). Since the input is always ASCII, we can just fake it + and pad every other byte with a NULL. If we ever support UTF-8 + in the GUI, we would have to perform a real UTF-8 to UCS-2 + conversion */ + for (i = 0; i < strlen(nt_password); i++) + { + password_unicode[i*2] = nt_password[i]; + password_unicode[i*2+1] = 0; + password_len_unicode += 2; + } + + /* Run MD4 against the resulting Unicode password. This will be + used to perform RC4 decryption on the password change block */ + crypt_md4(password_md4_hash, password_unicode, password_len_unicode); + + /* Copy the block into a temporary buffer so we can decrypt it */ + memset(block, 0, NT_BLOCK_SIZE); + tvb_memcpy(tvb, block, offset, NT_BLOCK_SIZE); + + /* RC4 decrypt the block with the old NT password hash */ + crypt_rc4(block, password_md4_hash, NT_BLOCK_SIZE); + + /* Show the decrypted buffer in a new window */ + decr_tvb = tvb_new_real_data(block, NT_BLOCK_SIZE, NT_BLOCK_SIZE); + tvb_set_free_cb(decr_tvb, g_free); + tvb_set_child_real_data_tvbuff(tvb, decr_tvb); + add_new_data_source(pinfo, decr_tvb, "Decrypted NT Password Block"); + + /* Dissect the decrypted block */ + offset += samr_dissect_decrypted_NT_PASSCHANGE_BLOCK(decr_tvb, 0, + pinfo, tree, + drep); + return offset; } @@ -4965,12 +5090,31 @@ proto_register_dcerpc_samr(void) NULL, 0, "NT Password Verifier", HFILL }}, { &hf_samr_lm_passchange_block, { - "Encrypted Block", "samr.lm_passchange_block", FT_BYTES, BASE_HEX, - NULL, 0, "Lan Manager Password Change Block", HFILL }}, + "Encrypted Block", "samr.lm_passchange_block", FT_BYTES, + BASE_HEX, NULL, 0, "Lan Manager Password Change Block", + HFILL }}, { &hf_samr_nt_passchange_block, { - "Encrypted Block", "samr.nt_passchange_block", FT_BYTES, BASE_HEX, - NULL, 0, "NT Password Change Block", HFILL }}, + "Encrypted Block", "samr.nt_passchange_block", FT_BYTES, + BASE_HEX, NULL, 0, "NT Password Change Block", HFILL }}, + + { &hf_samr_nt_passchange_block_decrypted, { + "Decrypted Block", "samr.nt_passchange_block_decrypted", + FT_BYTES, BASE_HEX, NULL, 0, + "NT Password Change Decrypted Block", HFILL }}, + + { &hf_samr_nt_passchange_block_newpass, { + "New NT Password", "samr.nt_passchange_block_new_ntpassword", + FT_STRING, BASE_NONE, NULL, 0, "New NT Password", HFILL }}, + + { &hf_samr_nt_passchange_block_newpass_len, { + "New NT Unicode Password length", + "samr.nt_passchange_block_new_ntpassword_len", FT_UINT32, + BASE_DEC, NULL, 0, "New NT Password Unicode Length", HFILL }}, + + { &hf_samr_nt_passchange_block_pseudorandom, { + "Pseudorandom data", "samr.nt_passchange_block_pseudorandom", + FT_BYTES, BASE_HEX, NULL, 0, "Pseudorandom data", HFILL }}, { &hf_samr_lm_change, { "LM Change", "samr.lm_change", FT_UINT8, BASE_HEX, @@ -5356,12 +5500,20 @@ proto_register_dcerpc_samr(void) &ett_samr_sid_and_attributes, &ett_nt_acct_ctrl }; + module_t *dcerpc_samr_module; proto_dcerpc_samr = proto_register_protocol( "Microsoft Security Account Manager", "SAMR", "samr"); proto_register_field_array (proto_dcerpc_samr, hf, array_length (hf)); proto_register_subtree_array(ett, array_length(ett)); + + dcerpc_samr_module = prefs_register_protocol(proto_dcerpc_samr, NULL); + + prefs_register_string_preference(dcerpc_samr_module, "nt_password", + "NT Password", + "NT Password (used to verify password changes)", + &nt_password); } void |