r23784: use the GPLv3 boilerplate as recommended by the FSF and the license text
[tprouty/samba.git] / source / utils / ntlm_auth.c
index 8296ccb5fba896c54a50903432c5cf65696ba453..d410e57f1cc19a5afd17616813b167bc145e41c3 100644 (file)
@@ -6,10 +6,11 @@
    Copyright (C) Tim Potter      2000-2003
    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003-2004
    Copyright (C) Francesco Chemolli <kinkie@kame.usr.dsi.unimi.it> 2000 
    Copyright (C) Tim Potter      2000-2003
    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003-2004
    Copyright (C) Francesco Chemolli <kinkie@kame.usr.dsi.unimi.it> 2000 
+   Copyright (C) Robert O'Callahan 2006 (added cached credential code).
 
    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
 
    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
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
@@ -18,8 +19,7 @@
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
    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.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "includes.h"
 */
 
 #include "includes.h"
@@ -38,6 +38,7 @@ enum stdio_helper_mode {
        GSS_SPNEGO,
        GSS_SPNEGO_CLIENT,
        NTLM_SERVER_1,
        GSS_SPNEGO,
        GSS_SPNEGO_CLIENT,
        NTLM_SERVER_1,
+       NTLM_CHANGE_PASSWORD_1,
        NUM_HELPER_MODES
 };
 
        NUM_HELPER_MODES
 };
 
@@ -62,6 +63,8 @@ static void manage_gss_spnego_client_request (enum stdio_helper_mode stdio_helpe
 static void manage_ntlm_server_1_request (enum stdio_helper_mode stdio_helper_mode, 
                                          char *buf, int length);
 
 static void manage_ntlm_server_1_request (enum stdio_helper_mode stdio_helper_mode, 
                                          char *buf, int length);
 
+static void manage_ntlm_change_password_1_request(enum stdio_helper_mode helper_mode, char *buf, int length);
+
 static const struct {
        enum stdio_helper_mode mode;
        const char *name;
 static const struct {
        enum stdio_helper_mode mode;
        const char *name;
@@ -74,6 +77,7 @@ static const struct {
        { GSS_SPNEGO, "gss-spnego", manage_gss_spnego_request},
        { GSS_SPNEGO_CLIENT, "gss-spnego-client", manage_gss_spnego_client_request},
        { NTLM_SERVER_1, "ntlm-server-1", manage_ntlm_server_1_request},
        { GSS_SPNEGO, "gss-spnego", manage_gss_spnego_request},
        { GSS_SPNEGO_CLIENT, "gss-spnego-client", manage_gss_spnego_client_request},
        { NTLM_SERVER_1, "ntlm-server-1", manage_ntlm_server_1_request},
+       { NTLM_CHANGE_PASSWORD_1, "ntlm-change-password-1", manage_ntlm_change_password_1_request},
        { NUM_HELPER_MODES, NULL, NULL}
 };
 
        { NUM_HELPER_MODES, NULL, NULL}
 };
 
@@ -88,6 +92,7 @@ static DATA_BLOB opt_lm_response;
 static DATA_BLOB opt_nt_response;
 static int request_lm_key;
 static int request_user_session_key;
 static DATA_BLOB opt_nt_response;
 static int request_lm_key;
 static int request_user_session_key;
+static int use_cached_creds;
 
 static const char *require_membership_of;
 static const char *require_membership_of_sid;
 
 static const char *require_membership_of;
 static const char *require_membership_of_sid;
@@ -105,7 +110,7 @@ static char winbind_separator(void)
 
        /* Send off request */
 
 
        /* Send off request */
 
-       if (winbindd_request(WINBINDD_INFO, NULL, &response) !=
+       if (winbindd_request_response(WINBINDD_INFO, NULL, &response) !=
            NSS_STATUS_SUCCESS) {
                d_printf("could not obtain winbind separator!\n");
                return *lp_winbind_separator();
            NSS_STATUS_SUCCESS) {
                d_printf("could not obtain winbind separator!\n");
                return *lp_winbind_separator();
@@ -135,7 +140,7 @@ const char *get_winbind_domain(void)
 
        /* Send off request */
 
 
        /* Send off request */
 
-       if (winbindd_request(WINBINDD_DOMAIN_NAME, NULL, &response) !=
+       if (winbindd_request_response(WINBINDD_DOMAIN_NAME, NULL, &response) !=
            NSS_STATUS_SUCCESS) {
                DEBUG(0, ("could not obtain winbind domain name!\n"));
                return lp_workgroup();
            NSS_STATUS_SUCCESS) {
                DEBUG(0, ("could not obtain winbind domain name!\n"));
                return lp_workgroup();
@@ -161,7 +166,7 @@ const char *get_winbind_netbios_name(void)
 
        /* Send off request */
 
 
        /* Send off request */
 
-       if (winbindd_request(WINBINDD_NETBIOS_NAME, NULL, &response) !=
+       if (winbindd_request_response(WINBINDD_NETBIOS_NAME, NULL, &response) !=
            NSS_STATUS_SUCCESS) {
                DEBUG(0, ("could not obtain winbind netbios name!\n"));
                return global_myname();
            NSS_STATUS_SUCCESS) {
                DEBUG(0, ("could not obtain winbind netbios name!\n"));
                return global_myname();
@@ -231,14 +236,14 @@ static BOOL get_require_membership_sid(void) {
                return False;
        }
 
                return False;
        }
 
-       if (winbindd_request(WINBINDD_LOOKUPNAME, &request, &response) !=
+       if (winbindd_request_response(WINBINDD_LOOKUPNAME, &request, &response) !=
            NSS_STATUS_SUCCESS) {
                DEBUG(0, ("Winbindd lookupname failed to resolve %s into a SID!\n", 
                          require_membership_of));
                return False;
        }
 
            NSS_STATUS_SUCCESS) {
                DEBUG(0, ("Winbindd lookupname failed to resolve %s into a SID!\n", 
                          require_membership_of));
                return False;
        }
 
-       require_membership_of_sid = strdup(response.data.sid.sid);
+       require_membership_of_sid = SMB_STRDUP(response.data.sid.sid);
 
        if (require_membership_of_sid)
                return True;
 
        if (require_membership_of_sid)
                return True;
@@ -266,9 +271,9 @@ static BOOL check_plaintext_auth(const char *user, const char *pass,
        fstrcpy(request.data.auth.user, user);
        fstrcpy(request.data.auth.pass, pass);
        if (require_membership_of_sid)
        fstrcpy(request.data.auth.user, user);
        fstrcpy(request.data.auth.pass, pass);
        if (require_membership_of_sid)
-               fstrcpy(request.data.auth.require_membership_of_sid, require_membership_of_sid);
+               pstrcpy(request.data.auth.require_membership_of_sid, require_membership_of_sid);
 
 
-       result = winbindd_request(WINBINDD_PAM_AUTH, &request, &response);
+       result = winbindd_request_response(WINBINDD_PAM_AUTH, &request, &response);
 
        /* Display response */
        
 
        /* Display response */
        
@@ -323,6 +328,8 @@ NTSTATUS contact_winbind_auth_crap(const char *username,
 
        request.flags = flags;
 
 
        request.flags = flags;
 
+       request.data.auth_crap.logon_parameters = MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT | MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT;
+
        if (require_membership_of_sid)
                fstrcpy(request.data.auth_crap.require_membership_of_sid, require_membership_of_sid);
 
        if (require_membership_of_sid)
                fstrcpy(request.data.auth_crap.require_membership_of_sid, require_membership_of_sid);
 
@@ -348,7 +355,7 @@ NTSTATUS contact_winbind_auth_crap(const char *username,
                 request.data.auth_crap.nt_resp_len = nt_response->length;
        }
        
                 request.data.auth_crap.nt_resp_len = nt_response->length;
        }
        
-       result = winbindd_request(WINBINDD_PAM_AUTH_CRAP, &request, &response);
+       result = winbindd_request_response(WINBINDD_PAM_AUTH_CRAP, &request, &response);
 
        /* Display response */
 
 
        /* Display response */
 
@@ -378,7 +385,7 @@ NTSTATUS contact_winbind_auth_crap(const char *username,
        }
 
        if (flags & WBFLAG_PAM_UNIX_NAME) {
        }
 
        if (flags & WBFLAG_PAM_UNIX_NAME) {
-               *unix_name = strdup((char *)response.extra_data);
+               *unix_name = SMB_STRDUP((char *)response.extra_data.data);
                if (!*unix_name) {
                        free_response(&response);
                        return NT_STATUS_NO_MEMORY;
                if (!*unix_name) {
                        free_response(&response);
                        return NT_STATUS_NO_MEMORY;
@@ -388,10 +395,90 @@ NTSTATUS contact_winbind_auth_crap(const char *username,
        free_response(&response);
        return nt_status;
 }
        free_response(&response);
        return nt_status;
 }
-                                  
+
+/* contact server to change user password using auth crap */
+static NTSTATUS contact_winbind_change_pswd_auth_crap(const char *username,
+                                                     const char *domain,
+                                                     const DATA_BLOB new_nt_pswd,
+                                                     const DATA_BLOB old_nt_hash_enc,
+                                                     const DATA_BLOB new_lm_pswd,
+                                                     const DATA_BLOB old_lm_hash_enc,
+                                                     char  **error_string)
+{
+       NTSTATUS nt_status;
+       NSS_STATUS result;
+       struct winbindd_request request;
+       struct winbindd_response response;
+
+       if (!get_require_membership_sid())
+       {
+               if(error_string)
+                       *error_string = smb_xstrdup("Can't get membership sid.");
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       ZERO_STRUCT(request);
+       ZERO_STRUCT(response);
+
+       if(username != NULL)
+               fstrcpy(request.data.chng_pswd_auth_crap.user, username);
+       if(domain != NULL)
+               fstrcpy(request.data.chng_pswd_auth_crap.domain,domain);
+
+       if(new_nt_pswd.length)
+       {
+               memcpy(request.data.chng_pswd_auth_crap.new_nt_pswd, new_nt_pswd.data, sizeof(request.data.chng_pswd_auth_crap.new_nt_pswd));
+               request.data.chng_pswd_auth_crap.new_nt_pswd_len = new_nt_pswd.length;
+       }
+
+       if(old_nt_hash_enc.length)
+       {
+               memcpy(request.data.chng_pswd_auth_crap.old_nt_hash_enc, old_nt_hash_enc.data, sizeof(request.data.chng_pswd_auth_crap.old_nt_hash_enc));
+               request.data.chng_pswd_auth_crap.old_nt_hash_enc_len = old_nt_hash_enc.length;
+       }
+
+       if(new_lm_pswd.length)
+       {
+               memcpy(request.data.chng_pswd_auth_crap.new_lm_pswd, new_lm_pswd.data, sizeof(request.data.chng_pswd_auth_crap.new_lm_pswd));
+               request.data.chng_pswd_auth_crap.new_lm_pswd_len = new_lm_pswd.length;
+       }
+
+       if(old_lm_hash_enc.length)
+       {
+               memcpy(request.data.chng_pswd_auth_crap.old_lm_hash_enc, old_lm_hash_enc.data, sizeof(request.data.chng_pswd_auth_crap.old_lm_hash_enc));
+               request.data.chng_pswd_auth_crap.old_lm_hash_enc_len = old_lm_hash_enc.length;
+       }
+       
+       result = winbindd_request_response(WINBINDD_PAM_CHNG_PSWD_AUTH_CRAP, &request, &response);
+
+       /* Display response */
+
+       if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0))
+       {
+               nt_status = NT_STATUS_UNSUCCESSFUL;
+               if (error_string)
+                       *error_string = smb_xstrdup("Reading winbind reply failed!");
+               free_response(&response);
+               return nt_status;
+       }
+       
+       nt_status = (NT_STATUS(response.data.auth.nt_status));
+       if (!NT_STATUS_IS_OK(nt_status))
+       {
+               if (error_string) 
+                       *error_string = smb_xstrdup(response.data.auth.error_string);
+               free_response(&response);
+               return nt_status;
+       }
+
+       free_response(&response);
+       
+    return nt_status;
+}
+
 static NTSTATUS winbind_pw_check(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *user_session_key, DATA_BLOB *lm_session_key) 
 {
 static NTSTATUS winbind_pw_check(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *user_session_key, DATA_BLOB *lm_session_key) 
 {
-       static const char zeros[16];
+       static const char zeros[16] = { 0, };
        NTSTATUS nt_status;
        char *error_string;
        uint8 lm_key[8]; 
        NTSTATUS nt_status;
        char *error_string;
        uint8 lm_key[8]; 
@@ -465,6 +552,7 @@ static NTSTATUS ntlm_auth_start_ntlmssp_client(NTLMSSP_STATE **client_ntlmssp_st
 {
        NTSTATUS status;
        if ( (opt_username == NULL) || (opt_domain == NULL) ) {
 {
        NTSTATUS status;
        if ( (opt_username == NULL) || (opt_domain == NULL) ) {
+               status = NT_STATUS_UNSUCCESSFUL;
                DEBUG(1, ("Need username and domain for NTLMSSP\n"));
                return NT_STATUS_INVALID_PARAMETER;
        }
                DEBUG(1, ("Need username and domain for NTLMSSP\n"));
                return NT_STATUS_INVALID_PARAMETER;
        }
@@ -496,14 +584,17 @@ static NTSTATUS ntlm_auth_start_ntlmssp_client(NTLMSSP_STATE **client_ntlmssp_st
                return status;
        }
 
                return status;
        }
 
-       status = ntlmssp_set_password(*client_ntlmssp_state, opt_password);
+       if (opt_password) {
+               status = ntlmssp_set_password(*client_ntlmssp_state, opt_password);
        
        
-       if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(1, ("Could not set password: %s\n",
-                         nt_errstr(status)));
-               ntlmssp_end(client_ntlmssp_state);
-               return status;
+               if (!NT_STATUS_IS_OK(status)) {
+                       DEBUG(1, ("Could not set password: %s\n",
+                                 nt_errstr(status)));
+                       ntlmssp_end(client_ntlmssp_state);
+                       return status;
+               }
        }
        }
+
        return NT_STATUS_OK;
 }
 
        return NT_STATUS_OK;
 }
 
@@ -512,7 +603,7 @@ static NTSTATUS ntlm_auth_start_ntlmssp_server(NTLMSSP_STATE **ntlmssp_state)
        NTSTATUS status = ntlmssp_server_start(ntlmssp_state);
        
        if (!NT_STATUS_IS_OK(status)) {
        NTSTATUS status = ntlmssp_server_start(ntlmssp_state);
        
        if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(1, ("Could not start NTLMSSP client: %s\n",
+               DEBUG(1, ("Could not start NTLMSSP server: %s\n",
                          nt_errstr(status)));
                return status;
        }
                          nt_errstr(status)));
                return status;
        }
@@ -530,10 +621,69 @@ static NTSTATUS ntlm_auth_start_ntlmssp_server(NTLMSSP_STATE **ntlmssp_state)
        return NT_STATUS_OK;
 }
 
        return NT_STATUS_OK;
 }
 
+/*******************************************************************
+ Used by firefox to drive NTLM auth to IIS servers.
+*******************************************************************/
+
+static NTSTATUS do_ccache_ntlm_auth(DATA_BLOB initial_msg, DATA_BLOB challenge_msg,
+                               DATA_BLOB *reply)
+{
+       struct winbindd_request wb_request;
+       struct winbindd_response wb_response;
+       NSS_STATUS result;
+
+       /* get winbindd to do the ntlmssp step on our behalf */
+       ZERO_STRUCT(wb_request);
+       ZERO_STRUCT(wb_response);
+
+       fstr_sprintf(wb_request.data.ccache_ntlm_auth.user,
+               "%s%c%s", opt_domain, winbind_separator(), opt_username);
+       wb_request.data.ccache_ntlm_auth.uid = geteuid();
+       wb_request.data.ccache_ntlm_auth.initial_blob_len = initial_msg.length;
+       wb_request.data.ccache_ntlm_auth.challenge_blob_len = challenge_msg.length;
+       wb_request.extra_len = initial_msg.length + challenge_msg.length;
+
+       if (wb_request.extra_len > 0) {
+               wb_request.extra_data.data = SMB_MALLOC_ARRAY(char, wb_request.extra_len);
+               if (wb_request.extra_data.data == NULL) {
+                       return NT_STATUS_NO_MEMORY;
+               }
+
+               memcpy(wb_request.extra_data.data, initial_msg.data, initial_msg.length);
+               memcpy(wb_request.extra_data.data + initial_msg.length,
+                       challenge_msg.data, challenge_msg.length);
+       }
+
+       result = winbindd_request_response(WINBINDD_CCACHE_NTLMAUTH, &wb_request, &wb_response);
+       SAFE_FREE(wb_request.extra_data.data);
+
+       if (result != NSS_STATUS_SUCCESS) {
+               free_response(&wb_response);
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       if (reply) {
+               *reply = data_blob(wb_response.extra_data.data,
+                               wb_response.data.ccache_ntlm_auth.auth_blob_len);
+               if (wb_response.data.ccache_ntlm_auth.auth_blob_len > 0 &&
+                               reply->data == NULL) {
+                       free_response(&wb_response);
+                       return NT_STATUS_NO_MEMORY;
+               }
+       }
+
+       free_response(&wb_response);
+       return NT_STATUS_MORE_PROCESSING_REQUIRED;
+}
+
 static void manage_squid_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode, 
                                         char *buf, int length) 
 {
        static NTLMSSP_STATE *ntlmssp_state = NULL;
 static void manage_squid_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode, 
                                         char *buf, int length) 
 {
        static NTLMSSP_STATE *ntlmssp_state = NULL;
+       static char* want_feature_list = NULL;
+       static uint32 neg_flags = 0;
+       static BOOL have_session_key = False;
+       static DATA_BLOB session_key;
        DATA_BLOB request, reply;
        NTSTATUS nt_status;
 
        DATA_BLOB request, reply;
        NTSTATUS nt_status;
 
@@ -544,15 +694,22 @@ static void manage_squid_ntlmssp_request(enum stdio_helper_mode stdio_helper_mod
        }
 
        if (strlen(buf) > 3) {
        }
 
        if (strlen(buf) > 3) {
+               if(strncmp(buf, "SF ", 3) == 0){
+                       DEBUG(10, ("Setting flags to negotioate\n"));
+                       SAFE_FREE(want_feature_list);
+                       want_feature_list = SMB_STRNDUP(buf+3, strlen(buf)-3);
+                       x_fprintf(x_stdout, "OK\n");
+                       return;
+               }
                request = base64_decode_data_blob(buf + 3);
        } else {
                request = base64_decode_data_blob(buf + 3);
        } else {
-               request = data_blob(NULL, 0);
+               request = data_blob_null;
        }
 
        if ((strncmp(buf, "PW ", 3) == 0)) {
                /* The calling application wants us to use a local password (rather than winbindd) */
 
        }
 
        if ((strncmp(buf, "PW ", 3) == 0)) {
                /* The calling application wants us to use a local password (rather than winbindd) */
 
-               opt_password = strndup((const char *)request.data, request.length);
+               opt_password = SMB_STRNDUP((const char *)request.data, request.length);
 
                if (opt_password == NULL) {
                        DEBUG(1, ("Out of memory\n"));
 
                if (opt_password == NULL) {
                        DEBUG(1, ("Out of memory\n"));
@@ -571,6 +728,23 @@ static void manage_squid_ntlmssp_request(enum stdio_helper_mode stdio_helper_mod
                        ntlmssp_end(&ntlmssp_state);
        } else if (strncmp(buf, "KK", 2) == 0) {
                
                        ntlmssp_end(&ntlmssp_state);
        } else if (strncmp(buf, "KK", 2) == 0) {
                
+       } else if (strncmp(buf, "GF", 2) == 0) {
+               DEBUG(10, ("Requested negotiated NTLMSSP flags\n"));
+               x_fprintf(x_stdout, "GF 0x%08lx\n", have_session_key?neg_flags:0l);
+               data_blob_free(&request);
+               return;
+       } else if (strncmp(buf, "GK", 2) == 0) {
+               DEBUG(10, ("Requested NTLMSSP session key\n"));
+               if(have_session_key) {
+                       char *key64 = base64_encode_data_blob(session_key);
+                       x_fprintf(x_stdout, "GK %s\n", key64?key64:"<NULL>");
+                       SAFE_FREE(key64);
+               } else {
+                       x_fprintf(x_stdout, "BH\n");
+               }
+                       
+               data_blob_free(&request);
+               return;
        } else {
                DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
                x_fprintf(x_stdout, "BH\n");
        } else {
                DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
                x_fprintf(x_stdout, "BH\n");
@@ -582,10 +756,11 @@ static void manage_squid_ntlmssp_request(enum stdio_helper_mode stdio_helper_mod
                        x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
                        return;
                }
                        x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
                        return;
                }
+               ntlmssp_want_feature_list(ntlmssp_state, want_feature_list);
        }
 
        DEBUG(10, ("got NTLMSSP packet:\n"));
        }
 
        DEBUG(10, ("got NTLMSSP packet:\n"));
-       dump_data(10, (const char *)request.data, request.length);
+       dump_data(10, request.data, request.length);
 
        nt_status = ntlmssp_update(ntlmssp_state, request, &reply);
        
 
        nt_status = ntlmssp_update(ntlmssp_state, request, &reply);
        
@@ -606,6 +781,13 @@ static void manage_squid_ntlmssp_request(enum stdio_helper_mode stdio_helper_mod
        } else {
                x_fprintf(x_stdout, "AF %s\n", (char *)ntlmssp_state->auth_context);
                DEBUG(10, ("NTLMSSP OK!\n"));
        } else {
                x_fprintf(x_stdout, "AF %s\n", (char *)ntlmssp_state->auth_context);
                DEBUG(10, ("NTLMSSP OK!\n"));
+               
+               if(have_session_key)
+                       data_blob_free(&session_key);
+               session_key = data_blob(ntlmssp_state->session_key.data, 
+                               ntlmssp_state->session_key.length);
+               neg_flags = ntlmssp_state->neg_flags;
+               have_session_key = True;
        }
 
        data_blob_free(&request);
        }
 
        data_blob_free(&request);
@@ -614,11 +796,25 @@ static void manage_squid_ntlmssp_request(enum stdio_helper_mode stdio_helper_mod
 static void manage_client_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode, 
                                         char *buf, int length) 
 {
 static void manage_client_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode, 
                                         char *buf, int length) 
 {
+       /* The statics here are *HORRIBLE* and this entire concept
+          needs to be rewritten. Essentially it's using these statics
+          as the state in a state machine. BLEEEGH ! JRA. */
+
        static NTLMSSP_STATE *ntlmssp_state = NULL;
        static NTLMSSP_STATE *ntlmssp_state = NULL;
+       static DATA_BLOB initial_message;
+       static char* want_feature_list = NULL;
+       static uint32 neg_flags = 0;
+       static BOOL have_session_key = False;
+       static DATA_BLOB session_key;
        DATA_BLOB request, reply;
        NTSTATUS nt_status;
        BOOL first = False;
        
        DATA_BLOB request, reply;
        NTSTATUS nt_status;
        BOOL first = False;
        
+       if (!opt_username || !*opt_username) {
+               x_fprintf(x_stderr, "username must be specified!\n\n");
+               exit(1);
+       }
+
        if (strlen(buf) < 2) {
                DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
                x_fprintf(x_stdout, "BH\n");
        if (strlen(buf) < 2) {
                DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
                x_fprintf(x_stdout, "BH\n");
@@ -626,15 +822,22 @@ static void manage_client_ntlmssp_request(enum stdio_helper_mode stdio_helper_mo
        }
 
        if (strlen(buf) > 3) {
        }
 
        if (strlen(buf) > 3) {
+               if(strncmp(buf, "SF ", 3) == 0) {
+                       DEBUG(10, ("Looking for flags to negotiate\n"));
+                       SAFE_FREE(want_feature_list);
+                       want_feature_list = SMB_STRNDUP(buf+3, strlen(buf)-3);
+                       x_fprintf(x_stdout, "OK\n");
+                       return;
+               }
                request = base64_decode_data_blob(buf + 3);
        } else {
                request = base64_decode_data_blob(buf + 3);
        } else {
-               request = data_blob(NULL, 0);
+               request = data_blob_null;
        }
 
        if (strncmp(buf, "PW ", 3) == 0) {
                /* We asked for a password and obviously got it :-) */
 
        }
 
        if (strncmp(buf, "PW ", 3) == 0) {
                /* We asked for a password and obviously got it :-) */
 
-               opt_password = strndup((const char *)request.data, request.length);
+               opt_password = SMB_STRNDUP((const char *)request.data, request.length);
 
                if (opt_password == NULL) {
                        DEBUG(1, ("Out of memory\n"));
 
                if (opt_password == NULL) {
                        DEBUG(1, ("Out of memory\n"));
@@ -648,7 +851,18 @@ static void manage_client_ntlmssp_request(enum stdio_helper_mode stdio_helper_mo
                return;
        }
 
                return;
        }
 
-       if (opt_password == NULL) {
+       if (!ntlmssp_state && use_cached_creds) {
+               /* check whether credentials are usable. */
+               DATA_BLOB empty_blob = data_blob_null;
+
+               nt_status = do_ccache_ntlm_auth(empty_blob, empty_blob, NULL);
+               if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+                       /* failed to use cached creds */
+                       use_cached_creds = False;
+               }
+       }
+
+       if (opt_password == NULL && !use_cached_creds) {
                
                /* Request a password from the calling process.  After
                   sending it, the calling process should retry asking for the negotiate. */
                
                /* Request a password from the calling process.  After
                   sending it, the calling process should retry asking for the negotiate. */
@@ -663,6 +877,25 @@ static void manage_client_ntlmssp_request(enum stdio_helper_mode stdio_helper_mo
                        ntlmssp_end(&ntlmssp_state);
        } else if (strncmp(buf, "TT", 2) == 0) {
                
                        ntlmssp_end(&ntlmssp_state);
        } else if (strncmp(buf, "TT", 2) == 0) {
                
+       } else if (strncmp(buf, "GF", 2) == 0) {
+               DEBUG(10, ("Requested negotiated NTLMSSP flags\n"));
+               x_fprintf(x_stdout, "GF 0x%08lx\n", have_session_key?neg_flags:0l);
+               data_blob_free(&request);
+               return;
+       } else if (strncmp(buf, "GK", 2) == 0 ) {
+               DEBUG(10, ("Requested session key\n"));
+
+               if(have_session_key) {
+                       char *key64 = base64_encode_data_blob(session_key);
+                       x_fprintf(x_stdout, "GK %s\n", key64?key64:"<NULL>");
+                       SAFE_FREE(key64);
+               }
+               else {
+                       x_fprintf(x_stdout, "BH\n");
+               }
+
+               data_blob_free(&request);
+               return;
        } else {
                DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
                x_fprintf(x_stdout, "BH\n");
        } else {
                DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
                x_fprintf(x_stdout, "BH\n");
@@ -674,13 +907,19 @@ static void manage_client_ntlmssp_request(enum stdio_helper_mode stdio_helper_mo
                        x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
                        return;
                }
                        x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
                        return;
                }
+               ntlmssp_want_feature_list(ntlmssp_state, want_feature_list);
                first = True;
                first = True;
+               initial_message = data_blob_null;
        }
 
        DEBUG(10, ("got NTLMSSP packet:\n"));
        }
 
        DEBUG(10, ("got NTLMSSP packet:\n"));
-       dump_data(10, (const char *)request.data, request.length);
+       dump_data(10, request.data, request.length);
 
 
-       nt_status = ntlmssp_update(ntlmssp_state, request, &reply);
+       if (use_cached_creds && !opt_password && !first) {
+               nt_status = do_ccache_ntlm_auth(initial_message, request, &reply);
+       } else {
+               nt_status = ntlmssp_update(ntlmssp_state, request, &reply);
+       }
        
        if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
                char *reply_base64 = base64_encode_data_blob(reply);
        
        if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
                char *reply_base64 = base64_encode_data_blob(reply);
@@ -690,10 +929,25 @@ static void manage_client_ntlmssp_request(enum stdio_helper_mode stdio_helper_mo
                        x_fprintf(x_stdout, "KK %s\n", reply_base64);
                }
                SAFE_FREE(reply_base64);
                        x_fprintf(x_stdout, "KK %s\n", reply_base64);
                }
                SAFE_FREE(reply_base64);
-               data_blob_free(&reply);
+               if (first) {
+                       initial_message = reply;
+               } else {
+                       data_blob_free(&reply);
+               }
                DEBUG(10, ("NTLMSSP challenge\n"));
        } else if (NT_STATUS_IS_OK(nt_status)) {
                DEBUG(10, ("NTLMSSP challenge\n"));
        } else if (NT_STATUS_IS_OK(nt_status)) {
-               x_fprintf(x_stdout, "AF\n");
+               char *reply_base64 = base64_encode_data_blob(reply);
+               x_fprintf(x_stdout, "AF %s\n", reply_base64);
+               SAFE_FREE(reply_base64);
+
+               if(have_session_key)
+                       data_blob_free(&session_key);
+
+               session_key = data_blob(ntlmssp_state->session_key.data, 
+                               ntlmssp_state->session_key.length);
+               neg_flags = ntlmssp_state->neg_flags;
+               have_session_key = True;
+
                DEBUG(10, ("NTLMSSP OK!\n"));
                if (ntlmssp_state)
                        ntlmssp_end(&ntlmssp_state);
                DEBUG(10, ("NTLMSSP OK!\n"));
                if (ntlmssp_state)
                        ntlmssp_end(&ntlmssp_state);
@@ -713,7 +967,7 @@ static void manage_squid_basic_request(enum stdio_helper_mode stdio_helper_mode,
        char *user, *pass;      
        user=buf;
        
        char *user, *pass;      
        user=buf;
        
-       pass=memchr(buf,' ',length);
+       pass=(char *)memchr(buf,' ',length);
        if (!pass) {
                DEBUG(2, ("Password not found. Denying access\n"));
                x_fprintf(x_stdout, "ERR\n");
        if (!pass) {
                DEBUG(2, ("Password not found. Denying access\n"));
                x_fprintf(x_stdout, "ERR\n");
@@ -753,7 +1007,7 @@ static void offer_gss_spnego_mechs(void) {
 
        /* Server negTokenInit (mech offerings) */
        spnego.type = SPNEGO_NEG_TOKEN_INIT;
 
        /* Server negTokenInit (mech offerings) */
        spnego.type = SPNEGO_NEG_TOKEN_INIT;
-       spnego.negTokenInit.mechTypes = smb_xmalloc(sizeof(char *) * 3);
+       spnego.negTokenInit.mechTypes = SMB_XMALLOC_ARRAY(const char *, 2);
 #ifdef HAVE_KRB5
        spnego.negTokenInit.mechTypes[0] = smb_xstrdup(OID_KERBEROS5_OLD);
        spnego.negTokenInit.mechTypes[1] = smb_xstrdup(OID_NTLMSSP);
 #ifdef HAVE_KRB5
        spnego.negTokenInit.mechTypes[0] = smb_xstrdup(OID_KERBEROS5_OLD);
        spnego.negTokenInit.mechTypes[1] = smb_xstrdup(OID_NTLMSSP);
@@ -857,6 +1111,7 @@ static void manage_gss_spnego_request(enum stdio_helper_mode stdio_helper_mode,
                        return;
                }
 
                        return;
                }
 
+               status = NT_STATUS_UNSUCCESSFUL;
                if (strcmp(request.negTokenInit.mechTypes[0], OID_NTLMSSP) == 0) {
 
                        if ( request.negTokenInit.mechToken.data == NULL ) {
                if (strcmp(request.negTokenInit.mechTypes[0], OID_NTLMSSP) == 0) {
 
                        if ( request.negTokenInit.mechToken.data == NULL ) {
@@ -879,12 +1134,12 @@ static void manage_gss_spnego_request(enum stdio_helper_mode stdio_helper_mode,
                        }
 
                        DEBUG(10, ("got NTLMSSP packet:\n"));
                        }
 
                        DEBUG(10, ("got NTLMSSP packet:\n"));
-                       dump_data(10, (const char *)request.negTokenInit.mechToken.data,
+                       dump_data(10, request.negTokenInit.mechToken.data,
                                  request.negTokenInit.mechToken.length);
 
                        response.type = SPNEGO_NEG_TOKEN_TARG;
                                  request.negTokenInit.mechToken.length);
 
                        response.type = SPNEGO_NEG_TOKEN_TARG;
-                       response.negTokenTarg.supportedMech = strdup(OID_NTLMSSP);
-                       response.negTokenTarg.mechListMIC = data_blob(NULL, 0);
+                       response.negTokenTarg.supportedMech = SMB_STRDUP(OID_NTLMSSP);
+                       response.negTokenTarg.mechListMIC = data_blob_null;
 
                        status = ntlmssp_update(ntlmssp_state,
                                                       request.negTokenInit.mechToken,
 
                        status = ntlmssp_update(ntlmssp_state,
                                                       request.negTokenInit.mechToken,
@@ -894,8 +1149,8 @@ static void manage_gss_spnego_request(enum stdio_helper_mode stdio_helper_mode,
 #ifdef HAVE_KRB5
                if (strcmp(request.negTokenInit.mechTypes[0], OID_KERBEROS5_OLD) == 0) {
 
 #ifdef HAVE_KRB5
                if (strcmp(request.negTokenInit.mechTypes[0], OID_KERBEROS5_OLD) == 0) {
 
+                       TALLOC_CTX *mem_ctx = talloc_init("manage_gss_spnego_request");
                        char *principal;
                        char *principal;
-                       DATA_BLOB auth_data;
                        DATA_BLOB ap_rep;
                        DATA_BLOB session_key;
 
                        DATA_BLOB ap_rep;
                        DATA_BLOB session_key;
 
@@ -906,14 +1161,16 @@ static void manage_gss_spnego_request(enum stdio_helper_mode stdio_helper_mode,
                        }
 
                        response.type = SPNEGO_NEG_TOKEN_TARG;
                        }
 
                        response.type = SPNEGO_NEG_TOKEN_TARG;
-                       response.negTokenTarg.supportedMech = strdup(OID_KERBEROS5_OLD);
-                       response.negTokenTarg.mechListMIC = data_blob(NULL, 0);
-                       response.negTokenTarg.responseToken = data_blob(NULL, 0);
+                       response.negTokenTarg.supportedMech = SMB_STRDUP(OID_KERBEROS5_OLD);
+                       response.negTokenTarg.mechListMIC = data_blob_null;
+                       response.negTokenTarg.responseToken = data_blob_null;
 
 
-                       status = ads_verify_ticket(lp_realm(),
+                       status = ads_verify_ticket(mem_ctx, lp_realm(), 0,
                                                   &request.negTokenInit.mechToken,
                                                   &request.negTokenInit.mechToken,
-                                                  &principal, &auth_data, &ap_rep,
-                                                  &session_key);
+                                                  &principal, NULL, &ap_rep,
+                                                  &session_key, True);
+
+                       talloc_destroy(mem_ctx);
 
                        /* Now in "principal" we have the name we are
                            authenticated as. */
 
                        /* Now in "principal" we have the name we are
                            authenticated as. */
@@ -930,11 +1187,10 @@ static void manage_gss_spnego_request(enum stdio_helper_mode stdio_helper_mode,
                                }
 
                                *domain++ = '\0';
                                }
 
                                *domain++ = '\0';
-                               domain = strdup(domain);
-                               user = strdup(principal);
+                               domain = SMB_STRDUP(domain);
+                               user = SMB_STRDUP(principal);
 
                                data_blob_free(&ap_rep);
 
                                data_blob_free(&ap_rep);
-                               data_blob_free(&auth_data);
 
                                SAFE_FREE(principal);
                        }
 
                                SAFE_FREE(principal);
                        }
@@ -964,12 +1220,12 @@ static void manage_gss_spnego_request(enum stdio_helper_mode stdio_helper_mode,
                                               &response.negTokenTarg.responseToken);
 
                response.type = SPNEGO_NEG_TOKEN_TARG;
                                               &response.negTokenTarg.responseToken);
 
                response.type = SPNEGO_NEG_TOKEN_TARG;
-               response.negTokenTarg.supportedMech = strdup(OID_NTLMSSP);
-               response.negTokenTarg.mechListMIC = data_blob(NULL, 0);
+               response.negTokenTarg.supportedMech = SMB_STRDUP(OID_NTLMSSP);
+               response.negTokenTarg.mechListMIC = data_blob_null;
 
                if (NT_STATUS_IS_OK(status)) {
 
                if (NT_STATUS_IS_OK(status)) {
-                       user = strdup(ntlmssp_state->user);
-                       domain = strdup(ntlmssp_state->domain);
+                       user = SMB_STRDUP(ntlmssp_state->user);
+                       domain = SMB_STRDUP(ntlmssp_state->domain);
                        ntlmssp_end(&ntlmssp_state);
                }
        }
                        ntlmssp_end(&ntlmssp_state);
                }
        }
@@ -1019,7 +1275,7 @@ static NTLMSSP_STATE *client_ntlmssp_state = NULL;
 static BOOL manage_client_ntlmssp_init(SPNEGO_DATA spnego)
 {
        NTSTATUS status;
 static BOOL manage_client_ntlmssp_init(SPNEGO_DATA spnego)
 {
        NTSTATUS status;
-       DATA_BLOB null_blob = data_blob(NULL, 0);
+       DATA_BLOB null_blob = data_blob_null;
        DATA_BLOB to_server;
        char *to_server_base64;
        const char *my_mechs[] = {OID_NTLMSSP, NULL};
        DATA_BLOB to_server;
        char *to_server_base64;
        const char *my_mechs[] = {OID_NTLMSSP, NULL};
@@ -1059,8 +1315,9 @@ static BOOL manage_client_ntlmssp_init(SPNEGO_DATA spnego)
        status = ntlmssp_update(client_ntlmssp_state, null_blob,
                                       &spnego.negTokenInit.mechToken);
 
        status = ntlmssp_update(client_ntlmssp_state, null_blob,
                                       &spnego.negTokenInit.mechToken);
 
-       if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
-               DEBUG(1, ("Expected MORE_PROCESSING_REQUIRED, got: %s\n",
+       if ( !(NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) ||
+                       NT_STATUS_IS_OK(status)) ) {
+               DEBUG(1, ("Expected OK or MORE_PROCESSING_REQUIRED, got: %s\n",
                          nt_errstr(status)));
                ntlmssp_end(&client_ntlmssp_state);
                return False;
                          nt_errstr(status)));
                ntlmssp_end(&client_ntlmssp_state);
                return False;
@@ -1079,7 +1336,7 @@ static BOOL manage_client_ntlmssp_init(SPNEGO_DATA spnego)
 static void manage_client_ntlmssp_targ(SPNEGO_DATA spnego)
 {
        NTSTATUS status;
 static void manage_client_ntlmssp_targ(SPNEGO_DATA spnego)
 {
        NTSTATUS status;
-       DATA_BLOB null_blob = data_blob(NULL, 0);
+       DATA_BLOB null_blob = data_blob_null;
        DATA_BLOB request;
        DATA_BLOB to_server;
        char *to_server_base64;
        DATA_BLOB request;
        DATA_BLOB to_server;
        char *to_server_base64;
@@ -1089,7 +1346,6 @@ static void manage_client_ntlmssp_targ(SPNEGO_DATA spnego)
        if (client_ntlmssp_state == NULL) {
                DEBUG(1, ("Got NTLMSSP tArg without a client state\n"));
                x_fprintf(x_stdout, "BH\n");
        if (client_ntlmssp_state == NULL) {
                DEBUG(1, ("Got NTLMSSP tArg without a client state\n"));
                x_fprintf(x_stdout, "BH\n");
-               ntlmssp_end(&client_ntlmssp_state);
                return;
        }
 
                return;
        }
 
@@ -1121,7 +1377,7 @@ static void manage_client_ntlmssp_targ(SPNEGO_DATA spnego)
 
        spnego.type = SPNEGO_NEG_TOKEN_TARG;
        spnego.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
 
        spnego.type = SPNEGO_NEG_TOKEN_TARG;
        spnego.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
-       spnego.negTokenTarg.supportedMech = OID_NTLMSSP;
+       spnego.negTokenTarg.supportedMech = (char *)OID_NTLMSSP;
        spnego.negTokenTarg.responseToken = request;
        spnego.negTokenTarg.mechListMIC = null_blob;
        
        spnego.negTokenTarg.responseToken = request;
        spnego.negTokenTarg.mechListMIC = null_blob;
        
@@ -1141,7 +1397,7 @@ static BOOL manage_client_krb5_init(SPNEGO_DATA spnego)
 {
        char *principal;
        DATA_BLOB tkt, to_server;
 {
        char *principal;
        DATA_BLOB tkt, to_server;
-       DATA_BLOB session_key_krb5 = data_blob(NULL, 0);
+       DATA_BLOB session_key_krb5 = data_blob_null;
        SPNEGO_DATA reply;
        char *reply_base64;
        int retval;
        SPNEGO_DATA reply;
        char *reply_base64;
        int retval;
@@ -1155,7 +1411,8 @@ static BOOL manage_client_krb5_init(SPNEGO_DATA spnego)
                return False;
        }
 
                return False;
        }
 
-       principal = malloc(spnego.negTokenInit.mechListMIC.length+1);
+       principal = (char *)SMB_MALLOC(
+               spnego.negTokenInit.mechListMIC.length+1);
 
        if (principal == NULL) {
                DEBUG(1, ("Could not malloc principal\n"));
 
        if (principal == NULL) {
                DEBUG(1, ("Could not malloc principal\n"));
@@ -1166,7 +1423,7 @@ static BOOL manage_client_krb5_init(SPNEGO_DATA spnego)
               spnego.negTokenInit.mechListMIC.length);
        principal[spnego.negTokenInit.mechListMIC.length] = '\0';
 
               spnego.negTokenInit.mechListMIC.length);
        principal[spnego.negTokenInit.mechListMIC.length] = '\0';
 
-       retval = cli_krb5_get_ticket(principal, 0, &tkt, &session_key_krb5);
+       retval = cli_krb5_get_ticket(principal, 0, &tkt, &session_key_krb5, 0, NULL, NULL);
 
        if (retval) {
 
 
        if (retval) {
 
@@ -1183,13 +1440,12 @@ static BOOL manage_client_krb5_init(SPNEGO_DATA spnego)
 
                pstr_sprintf(user, "%s@%s", opt_username, opt_domain);
 
 
                pstr_sprintf(user, "%s@%s", opt_username, opt_domain);
 
-               if ((retval = kerberos_kinit_password(user, opt_password, 
-                                                     0, NULL))) {
+               if ((retval = kerberos_kinit_password(user, opt_password, 0, NULL))) {
                        DEBUG(10, ("Requesting TGT failed: %s\n", error_message(retval)));
                        return False;
                }
 
                        DEBUG(10, ("Requesting TGT failed: %s\n", error_message(retval)));
                        return False;
                }
 
-               retval = cli_krb5_get_ticket(principal, 0, &tkt, &session_key_krb5);
+               retval = cli_krb5_get_ticket(principal, 0, &tkt, &session_key_krb5, 0, NULL, NULL);
 
                if (retval) {
                        DEBUG(10, ("Kinit suceeded, but getting a ticket failed: %s\n", error_message(retval)));
 
                if (retval) {
                        DEBUG(10, ("Kinit suceeded, but getting a ticket failed: %s\n", error_message(retval)));
@@ -1205,7 +1461,7 @@ static BOOL manage_client_krb5_init(SPNEGO_DATA spnego)
        reply.negTokenInit.mechTypes = my_mechs;
        reply.negTokenInit.reqFlags = 0;
        reply.negTokenInit.mechToken = tkt;
        reply.negTokenInit.mechTypes = my_mechs;
        reply.negTokenInit.reqFlags = 0;
        reply.negTokenInit.mechToken = tkt;
-       reply.negTokenInit.mechListMIC = data_blob(NULL, 0);
+       reply.negTokenInit.mechListMIC = data_blob_null;
 
        len = write_spnego_data(&to_server, &reply);
        data_blob_free(&tkt);
 
        len = write_spnego_data(&to_server, &reply);
        data_blob_free(&tkt);
@@ -1254,6 +1510,11 @@ static void manage_gss_spnego_client_request(enum stdio_helper_mode stdio_helper
        SPNEGO_DATA spnego;
        ssize_t len;
 
        SPNEGO_DATA spnego;
        ssize_t len;
 
+       if (!opt_username || !*opt_username) {
+               x_fprintf(x_stderr, "username must be specified!\n\n");
+               exit(1);
+       }
+
        if (strlen(buf) <= 3) {
                DEBUG(1, ("SPNEGO query [%s] too short\n", buf));
                x_fprintf(x_stdout, "BH\n");
        if (strlen(buf) <= 3) {
                DEBUG(1, ("SPNEGO query [%s] too short\n", buf));
                x_fprintf(x_stdout, "BH\n");
@@ -1266,7 +1527,7 @@ static void manage_gss_spnego_client_request(enum stdio_helper_mode stdio_helper
 
                /* We asked for a password and obviously got it :-) */
 
 
                /* We asked for a password and obviously got it :-) */
 
-               opt_password = strndup((const char *)request.data, request.length);
+               opt_password = SMB_STRNDUP((const char *)request.data, request.length);
                
                if (opt_password == NULL) {
                        DEBUG(1, ("Out of memory\n"));
                
                if (opt_password == NULL) {
                        DEBUG(1, ("Out of memory\n"));
@@ -1305,7 +1566,7 @@ static void manage_gss_spnego_client_request(enum stdio_helper_mode stdio_helper
 
                /* The server offers a list of mechanisms */
 
 
                /* The server offers a list of mechanisms */
 
-               const char **mechType = spnego.negTokenInit.mechTypes;
+               const char **mechType = (const char **)spnego.negTokenInit.mechTypes;
 
                while (*mechType != NULL) {
 
 
                while (*mechType != NULL) {
 
@@ -1470,28 +1731,28 @@ static void manage_ntlm_server_1_request(enum stdio_helper_mode stdio_helper_mod
                                if (ntlm_server_1_lm_session_key 
                                    && (memcmp(zeros, lm_key, 
                                               sizeof(lm_key)) != 0)) {
                                if (ntlm_server_1_lm_session_key 
                                    && (memcmp(zeros, lm_key, 
                                               sizeof(lm_key)) != 0)) {
-                                       hex_encode((const unsigned char *)lm_key,
-                                                  sizeof(lm_key),
-                                                  &hex_lm_key);
+                                       hex_lm_key = hex_encode(NULL,
+                                                               (const unsigned char *)lm_key,
+                                                               sizeof(lm_key));
                                        x_fprintf(x_stdout, "LANMAN-Session-Key: %s\n", hex_lm_key);
                                        x_fprintf(x_stdout, "LANMAN-Session-Key: %s\n", hex_lm_key);
-                                       SAFE_FREE(hex_lm_key);
+                                       TALLOC_FREE(hex_lm_key);
                                }
 
                                if (ntlm_server_1_user_session_key 
                                    && (memcmp(zeros, user_session_key, 
                                               sizeof(user_session_key)) != 0)) {
                                }
 
                                if (ntlm_server_1_user_session_key 
                                    && (memcmp(zeros, user_session_key, 
                                               sizeof(user_session_key)) != 0)) {
-                                       hex_encode((const unsigned char *)user_session_key, 
-                                                  sizeof(user_session_key)
-                                                  &hex_user_session_key);
+                                       hex_user_session_key = hex_encode(NULL,
+                                                                         (const unsigned char *)user_session_key
+                                                                         sizeof(user_session_key));
                                        x_fprintf(x_stdout, "User-Session-Key: %s\n", hex_user_session_key);
                                        x_fprintf(x_stdout, "User-Session-Key: %s\n", hex_user_session_key);
-                                       SAFE_FREE(hex_user_session_key);
+                                       TALLOC_FREE(hex_user_session_key);
                                }
                        }
                }
                /* clear out the state */
                                }
                        }
                }
                /* clear out the state */
-               challenge = data_blob(NULL, 0);
-               nt_response = data_blob(NULL, 0);
-               lm_response = data_blob(NULL, 0);
+               challenge = data_blob_null;
+               nt_response = data_blob_null;
+               lm_response = data_blob_null;
                SAFE_FREE(full_username);
                SAFE_FREE(username);
                SAFE_FREE(domain);
                SAFE_FREE(full_username);
                SAFE_FREE(username);
                SAFE_FREE(domain);
@@ -1533,28 +1794,28 @@ static void manage_ntlm_server_1_request(enum stdio_helper_mode stdio_helper_mod
        }
 
        if (strequal(request, "LANMAN-Challenge")) {
        }
 
        if (strequal(request, "LANMAN-Challenge")) {
-               challenge = strhex_to_data_blob(parameter);
+               challenge = strhex_to_data_blob(NULL, parameter);
                if (challenge.length != 8) {
                        x_fprintf(x_stdout, "Error: hex decode of %s failed! (got %d bytes, expected 8)\n.\n", 
                                  parameter,
                                  (int)challenge.length);
                if (challenge.length != 8) {
                        x_fprintf(x_stdout, "Error: hex decode of %s failed! (got %d bytes, expected 8)\n.\n", 
                                  parameter,
                                  (int)challenge.length);
-                       challenge = data_blob(NULL, 0);
+                       challenge = data_blob_null;
                }
        } else if (strequal(request, "NT-Response")) {
                }
        } else if (strequal(request, "NT-Response")) {
-               nt_response = strhex_to_data_blob(parameter);
+               nt_response = strhex_to_data_blob(NULL, parameter);
                if (nt_response.length < 24) {
                        x_fprintf(x_stdout, "Error: hex decode of %s failed! (only got %d bytes, needed at least 24)\n.\n", 
                                  parameter,
                                  (int)nt_response.length);
                if (nt_response.length < 24) {
                        x_fprintf(x_stdout, "Error: hex decode of %s failed! (only got %d bytes, needed at least 24)\n.\n", 
                                  parameter,
                                  (int)nt_response.length);
-                       nt_response = data_blob(NULL, 0);
+                       nt_response = data_blob_null;
                }
        } else if (strequal(request, "LANMAN-Response")) {
                }
        } else if (strequal(request, "LANMAN-Response")) {
-               lm_response = strhex_to_data_blob(parameter);
+               lm_response = strhex_to_data_blob(NULL, parameter);
                if (lm_response.length != 24) {
                        x_fprintf(x_stdout, "Error: hex decode of %s failed! (got %d bytes, expected 24)\n.\n", 
                                  parameter,
                                  (int)lm_response.length);
                if (lm_response.length != 24) {
                        x_fprintf(x_stdout, "Error: hex decode of %s failed! (got %d bytes, expected 24)\n.\n", 
                                  parameter,
                                  (int)lm_response.length);
-                       lm_response = data_blob(NULL, 0);
+                       lm_response = data_blob_null;
                }
        } else if (strequal(request, "Password")) {
                plaintext_password = smb_xstrdup(parameter);
                }
        } else if (strequal(request, "Password")) {
                plaintext_password = smb_xstrdup(parameter);
@@ -1573,6 +1834,216 @@ static void manage_ntlm_server_1_request(enum stdio_helper_mode stdio_helper_mod
        }
 }
 
        }
 }
 
+static void manage_ntlm_change_password_1_request(enum stdio_helper_mode helper_mode, char *buf, int length)
+{
+       char *request, *parameter;      
+       static DATA_BLOB new_nt_pswd;
+       static DATA_BLOB old_nt_hash_enc;
+       static DATA_BLOB new_lm_pswd;
+       static DATA_BLOB old_lm_hash_enc;
+       static char *full_username = NULL;
+       static char *username = NULL;
+       static char *domain = NULL;
+       static char *newpswd =  NULL;
+       static char *oldpswd = NULL;
+
+       if (strequal(buf, ".")) {
+               if(newpswd && oldpswd) {
+                       uchar old_nt_hash[16];
+                       uchar old_lm_hash[16];
+                       uchar new_nt_hash[16];
+                       uchar new_lm_hash[16];
+
+                       new_nt_pswd = data_blob(NULL, 516);
+                       old_nt_hash_enc = data_blob(NULL, 16);
+                       
+                       /* Calculate the MD4 hash (NT compatible) of the
+                        * password */
+                       E_md4hash(oldpswd, old_nt_hash);
+                       E_md4hash(newpswd, new_nt_hash);
+
+                       /* E_deshash returns false for 'long'
+                          passwords (> 14 DOS chars).  
+                          
+                          Therefore, don't send a buffer
+                          encrypted with the truncated hash
+                          (it could allow an even easier
+                          attack on the password)
+
+                          Likewise, obey the admin's restriction
+                       */
+
+                       if (lp_client_lanman_auth() &&
+                           E_deshash(newpswd, new_lm_hash) &&
+                           E_deshash(oldpswd, old_lm_hash)) {
+                               new_lm_pswd = data_blob(NULL, 516);
+                               old_lm_hash_enc = data_blob(NULL, 16);
+                               encode_pw_buffer(new_lm_pswd.data, newpswd,
+                                                STR_UNICODE);
+
+                               SamOEMhash(new_lm_pswd.data, old_nt_hash, 516);
+                               E_old_pw_hash(new_nt_hash, old_lm_hash,
+                                             old_lm_hash_enc.data);
+                       } else {
+                               new_lm_pswd.data = NULL;
+                               new_lm_pswd.length = 0;
+                               old_lm_hash_enc.data = NULL;
+                               old_lm_hash_enc.length = 0;
+                       }
+
+                       encode_pw_buffer(new_nt_pswd.data, newpswd,
+                                        STR_UNICODE);
+       
+                       SamOEMhash(new_nt_pswd.data, old_nt_hash, 516);
+                       E_old_pw_hash(new_nt_hash, old_nt_hash,
+                                     old_nt_hash_enc.data);
+               }
+               
+               if (!full_username && !username) {      
+                       x_fprintf(x_stdout, "Error: No username supplied!\n");
+               } else if ((!new_nt_pswd.data || !old_nt_hash_enc.data) &&
+                          (!new_lm_pswd.data || old_lm_hash_enc.data) ) {
+                       x_fprintf(x_stdout, "Error: No NT or LM password "
+                                 "blobs supplied!\n");
+               } else {
+                       char *error_string = NULL;
+                       
+                       if (full_username && !username) {
+                               fstring fstr_user;
+                               fstring fstr_domain;
+                               
+                               if (!parse_ntlm_auth_domain_user(full_username,
+                                                                fstr_user,
+                                                                fstr_domain)) {
+                                       /* username might be 'tainted', don't
+                                        * print into our new-line
+                                        * deleimianted stream */
+                                       x_fprintf(x_stdout, "Error: Could not "
+                                                 "parse into domain and "
+                                                 "username\n");
+                                       SAFE_FREE(username);
+                                       username = smb_xstrdup(full_username);
+                               } else {
+                                       SAFE_FREE(username);
+                                       SAFE_FREE(domain);
+                                       username = smb_xstrdup(fstr_user);
+                                       domain = smb_xstrdup(fstr_domain);
+                               }
+                               
+                       }
+
+                       if(!NT_STATUS_IS_OK(contact_winbind_change_pswd_auth_crap(
+                                                   username, domain,
+                                                   new_nt_pswd,
+                                                   old_nt_hash_enc,
+                                                   new_lm_pswd,
+                                                   old_lm_hash_enc,
+                                                   &error_string))) {
+                               x_fprintf(x_stdout, "Password-Change: No\n");
+                               x_fprintf(x_stdout, "Password-Change-Error: "
+                                         "%s\n.\n", error_string);
+                       } else {
+                               x_fprintf(x_stdout, "Password-Change: Yes\n");
+                       }
+
+                       SAFE_FREE(error_string);
+               }
+               /* clear out the state */
+               new_nt_pswd = data_blob_null;
+               old_nt_hash_enc = data_blob_null;
+               new_lm_pswd = data_blob_null;
+               old_nt_hash_enc = data_blob_null;
+               SAFE_FREE(full_username);
+               SAFE_FREE(username);
+               SAFE_FREE(domain);
+               SAFE_FREE(newpswd);
+               SAFE_FREE(oldpswd);
+               x_fprintf(x_stdout, ".\n");
+
+               return;
+       }
+
+       request = buf;
+
+       /* Indicates a base64 encoded structure */
+       parameter = strstr_m(request, ":: ");
+       if (!parameter) {
+               parameter = strstr_m(request, ": ");
+               
+               if (!parameter) {
+                       DEBUG(0, ("Parameter not found!\n"));
+                       x_fprintf(x_stdout, "Error: Parameter not found!\n.\n");
+                       return;
+               }
+               
+               parameter[0] ='\0';
+               parameter++;
+               parameter[0] ='\0';
+               parameter++;
+       } else {
+               parameter[0] ='\0';
+               parameter++;
+               parameter[0] ='\0';
+               parameter++;
+               parameter[0] ='\0';
+               parameter++;
+
+               base64_decode_inplace(parameter);
+       }
+
+       if (strequal(request, "new-nt-password-blob")) {
+               new_nt_pswd = strhex_to_data_blob(NULL, parameter);
+               if (new_nt_pswd.length != 516) {
+                       x_fprintf(x_stdout, "Error: hex decode of %s failed! "
+                                 "(got %d bytes, expected 516)\n.\n", 
+                                 parameter,
+                                 (int)new_nt_pswd.length);
+                       new_nt_pswd = data_blob_null;
+               }
+       } else if (strequal(request, "old-nt-hash-blob")) {
+               old_nt_hash_enc = strhex_to_data_blob(NULL, parameter);
+               if (old_nt_hash_enc.length != 16) {
+                       x_fprintf(x_stdout, "Error: hex decode of %s failed! "
+                                 "(got %d bytes, expected 16)\n.\n", 
+                                 parameter,
+                                 (int)old_nt_hash_enc.length);
+                       old_nt_hash_enc = data_blob_null;
+               }
+       } else if (strequal(request, "new-lm-password-blob")) {
+               new_lm_pswd = strhex_to_data_blob(NULL, parameter);
+               if (new_lm_pswd.length != 516) {
+                       x_fprintf(x_stdout, "Error: hex decode of %s failed! "
+                                 "(got %d bytes, expected 516)\n.\n", 
+                                 parameter,
+                                 (int)new_lm_pswd.length);
+                       new_lm_pswd = data_blob_null;
+               }
+       }
+       else if (strequal(request, "old-lm-hash-blob")) {
+               old_lm_hash_enc = strhex_to_data_blob(NULL, parameter);
+               if (old_lm_hash_enc.length != 16)
+               {
+                       x_fprintf(x_stdout, "Error: hex decode of %s failed! "
+                                 "(got %d bytes, expected 16)\n.\n", 
+                                 parameter,
+                                 (int)old_lm_hash_enc.length);
+                       old_lm_hash_enc = data_blob_null;
+               }
+       } else if (strequal(request, "nt-domain")) {
+               domain = smb_xstrdup(parameter);
+       } else if(strequal(request, "username")) {
+               username = smb_xstrdup(parameter);
+       } else if(strequal(request, "full-username")) {
+               username = smb_xstrdup(parameter);
+       } else if(strequal(request, "new-password")) {
+               newpswd = smb_xstrdup(parameter);
+       } else if (strequal(request, "old-password")) {
+               oldpswd = smb_xstrdup(parameter);
+       } else {
+               x_fprintf(x_stdout, "Error: Unknown request %s\n.\n", request);
+       }
+}
+
 static void manage_squid_request(enum stdio_helper_mode helper_mode, stdio_helper_function fn) 
 {
        char buf[SQUID_BUFFER_SIZE+1];
 static void manage_squid_request(enum stdio_helper_mode helper_mode, stdio_helper_function fn) 
 {
        char buf[SQUID_BUFFER_SIZE+1];
@@ -1591,7 +2062,7 @@ static void manage_squid_request(enum stdio_helper_mode helper_mode, stdio_helpe
                exit(0);
        }
     
                exit(0);
        }
     
-       c=memchr(buf,'\n',sizeof(buf)-1);
+       c=(char *)memchr(buf,'\n',sizeof(buf)-1);
        if (c) {
                *c = '\0';
                length = c-buf;
        if (c) {
                *c = '\0';
                length = c-buf;
@@ -1672,20 +2143,18 @@ static BOOL check_auth_crap(void)
        if (request_lm_key 
            && (memcmp(zeros, lm_key, 
                       sizeof(lm_key)) != 0)) {
        if (request_lm_key 
            && (memcmp(zeros, lm_key, 
                       sizeof(lm_key)) != 0)) {
-               hex_encode((const unsigned char *)lm_key,
-                          sizeof(lm_key),
-                          &hex_lm_key);
+               hex_lm_key = hex_encode(NULL, (const unsigned char *)lm_key,
+                                       sizeof(lm_key));
                x_fprintf(x_stdout, "LM_KEY: %s\n", hex_lm_key);
                x_fprintf(x_stdout, "LM_KEY: %s\n", hex_lm_key);
-               SAFE_FREE(hex_lm_key);
+               TALLOC_FREE(hex_lm_key);
        }
        if (request_user_session_key 
            && (memcmp(zeros, user_session_key, 
                       sizeof(user_session_key)) != 0)) {
        }
        if (request_user_session_key 
            && (memcmp(zeros, user_session_key, 
                       sizeof(user_session_key)) != 0)) {
-               hex_encode((const unsigned char *)user_session_key, 
-                          sizeof(user_session_key), 
-                          &hex_user_session_key);
+               hex_user_session_key = hex_encode(NULL, (const unsigned char *)user_session_key, 
+                                                 sizeof(user_session_key));
                x_fprintf(x_stdout, "NT_KEY: %s\n", hex_user_session_key);
                x_fprintf(x_stdout, "NT_KEY: %s\n", hex_user_session_key);
-               SAFE_FREE(hex_user_session_key);
+               TALLOC_FREE(hex_user_session_key);
        }
 
         return True;
        }
 
         return True;
@@ -1705,7 +2174,8 @@ enum {
        OPT_LM_KEY,
        OPT_USER_SESSION_KEY,
        OPT_DIAGNOSTICS,
        OPT_LM_KEY,
        OPT_USER_SESSION_KEY,
        OPT_DIAGNOSTICS,
-       OPT_REQUIRE_MEMBERSHIP
+       OPT_REQUIRE_MEMBERSHIP,
+       OPT_USE_CACHED_CREDS
 };
 
  int main(int argc, const char **argv)
 };
 
  int main(int argc, const char **argv)
@@ -1738,8 +2208,9 @@ enum {
                { "lm-response", 0, POPT_ARG_STRING, &hex_lm_response, OPT_LM, "LM Response to the challenge (HEX encoded)"},
                { "nt-response", 0, POPT_ARG_STRING, &hex_nt_response, OPT_NT, "NT or NTLMv2 Response to the challenge (HEX encoded)"},
                { "password", 0, POPT_ARG_STRING, &opt_password, OPT_PASSWORD, "User's plaintext password"},            
                { "lm-response", 0, POPT_ARG_STRING, &hex_lm_response, OPT_LM, "LM Response to the challenge (HEX encoded)"},
                { "nt-response", 0, POPT_ARG_STRING, &hex_nt_response, OPT_NT, "NT or NTLMv2 Response to the challenge (HEX encoded)"},
                { "password", 0, POPT_ARG_STRING, &opt_password, OPT_PASSWORD, "User's plaintext password"},            
-               { "request-lm-key", 0, POPT_ARG_NONE, &request_lm_key, OPT_LM_KEY, "Retreive LM session key"},
-               { "request-nt-key", 0, POPT_ARG_NONE, &request_user_session_key, OPT_USER_SESSION_KEY, "Retreive User (NT) session key"},
+               { "request-lm-key", 0, POPT_ARG_NONE, &request_lm_key, OPT_LM_KEY, "Retrieve LM session key"},
+               { "request-nt-key", 0, POPT_ARG_NONE, &request_user_session_key, OPT_USER_SESSION_KEY, "Retrieve User (NT) session key"},
+               { "use-cached-creds", 0, POPT_ARG_NONE, &use_cached_creds, OPT_USE_CACHED_CREDS, "Use cached credentials if no password is given"},
                { "diagnostics", 0, POPT_ARG_NONE, &diagnostics, OPT_DIAGNOSTICS, "Perform diagnostics on the authentictaion chain"},
                { "require-membership-of", 0, POPT_ARG_STRING, &require_membership_of, OPT_REQUIRE_MEMBERSHIP, "Require that a user be a member of this group (either name or SID) for authentication to succeed" },
                POPT_COMMON_SAMBA
                { "diagnostics", 0, POPT_ARG_NONE, &diagnostics, OPT_DIAGNOSTICS, "Perform diagnostics on the authentictaion chain"},
                { "require-membership-of", 0, POPT_ARG_STRING, &require_membership_of, OPT_REQUIRE_MEMBERSHIP, "Require that a user be a member of this group (either name or SID) for authentication to succeed" },
                POPT_COMMON_SAMBA
@@ -1747,12 +2218,13 @@ enum {
        };
 
        /* Samba client initialisation */
        };
 
        /* Samba client initialisation */
+       load_case_tables();
 
        dbf = x_stderr;
        
        /* Samba client initialisation */
 
 
        dbf = x_stderr;
        
        /* Samba client initialisation */
 
-       if (!lp_load(dyn_CONFIGFILE, True, False, False)) {
+       if (!lp_load(dyn_CONFIGFILE, True, False, False, True)) {
                d_fprintf(stderr, "ntlm_auth: error opening config file %s. Error was %s\n",
                        dyn_CONFIGFILE, strerror(errno));
                exit(1);
                d_fprintf(stderr, "ntlm_auth: error opening config file %s. Error was %s\n",
                        dyn_CONFIGFILE, strerror(errno));
                exit(1);
@@ -1775,7 +2247,7 @@ enum {
        while((opt = poptGetNextOpt(pc)) != -1) {
                switch (opt) {
                case OPT_CHALLENGE:
        while((opt = poptGetNextOpt(pc)) != -1) {
                switch (opt) {
                case OPT_CHALLENGE:
-                       opt_challenge = strhex_to_data_blob(hex_challenge);
+                       opt_challenge = strhex_to_data_blob(NULL, hex_challenge);
                        if (opt_challenge.length != 8) {
                                x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n", 
                                          hex_challenge,
                        if (opt_challenge.length != 8) {
                                x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n", 
                                          hex_challenge,
@@ -1784,7 +2256,7 @@ enum {
                        }
                        break;
                case OPT_LM: 
                        }
                        break;
                case OPT_LM: 
-                       opt_lm_response = strhex_to_data_blob(hex_lm_response);
+                       opt_lm_response = strhex_to_data_blob(NULL, hex_lm_response);
                        if (opt_lm_response.length != 24) {
                                x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n", 
                                          hex_lm_response,
                        if (opt_lm_response.length != 24) {
                                x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n", 
                                          hex_lm_response,
@@ -1794,7 +2266,7 @@ enum {
                        break;
 
                case OPT_NT: 
                        break;
 
                case OPT_NT: 
-                       opt_nt_response = strhex_to_data_blob(hex_nt_response);
+                       opt_nt_response = strhex_to_data_blob(NULL, hex_nt_response);
                        if (opt_nt_response.length < 24) {
                                x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n", 
                                          hex_nt_response,
                        if (opt_nt_response.length < 24) {
                                x_fprintf(x_stderr, "hex decode of %s failed! (only got %d bytes)\n", 
                                          hex_nt_response,
@@ -1811,6 +2283,34 @@ enum {
                }
        }
 
                }
        }
 
+       if (opt_username) {
+               char *domain = SMB_STRDUP(opt_username);
+               char *p = strchr_m(domain, *lp_winbind_separator());
+               if (p) {
+                       opt_username = p+1;
+                       *p = '\0';
+                       if (opt_domain && !strequal(opt_domain, domain)) {
+                               x_fprintf(x_stderr, "Domain specified in username (%s) "
+                                       "doesn't match specified domain (%s)!\n\n",
+                                       domain, opt_domain);
+                               poptPrintHelp(pc, stderr, 0);
+                               exit(1);
+                       }
+                       opt_domain = domain;
+               } else {
+                       SAFE_FREE(domain);
+               }
+       }
+
+       /* Note: if opt_domain is "" then send no domain */
+       if (opt_domain == NULL) {
+               opt_domain = get_winbind_domain();
+       }
+
+       if (opt_workstation == NULL) {
+               opt_workstation = "";
+       }
+
        if (helper_protocol) {
                int i;
                for (i=0; i<NUM_HELPER_MODES; i++) {
        if (helper_protocol) {
                int i;
                for (i=0; i<NUM_HELPER_MODES; i++) {
@@ -1828,20 +2328,12 @@ enum {
                exit(1);
        }
 
                exit(1);
        }
 
-       if (!opt_username) {
+       if (!opt_username || !*opt_username) {
                x_fprintf(x_stderr, "username must be specified!\n\n");
                poptPrintHelp(pc, stderr, 0);
                exit(1);
        }
 
                x_fprintf(x_stderr, "username must be specified!\n\n");
                poptPrintHelp(pc, stderr, 0);
                exit(1);
        }
 
-       if (opt_domain == NULL) {
-               opt_domain = get_winbind_domain();
-       }
-
-       if (opt_workstation == NULL) {
-               opt_workstation = "";
-       }
-
        if (opt_challenge.length) {
                if (!check_auth_crap()) {
                        exit(1);
        if (opt_challenge.length) {
                if (!check_auth_crap()) {
                        exit(1);