NTLM Authentication:
authorAndrew Bartlett <abartlet@samba.org>
Sun, 23 Mar 2003 13:03:25 +0000 (13:03 +0000)
committerAndrew Bartlett <abartlet@samba.org>
Sun, 23 Mar 2003 13:03:25 +0000 (13:03 +0000)
- Add a 'privileged' mode to Winbindd.  This is achieved by means of a directory
  under lockdir, that the admin can change the group access for.

- This mode is now required to access with 'CRAP' authentication feature.
- This *will* break the current SQUID helper, so I've fixed up our ntlm_auth
  replacement:
 - Update our NTLMSSP code to cope with 'datagram' mode, where we don't get a
   challenge.
 - Use this to make our ntlm_auth utility suitable for use in current Squid 2.5
   servers.
 - Tested - works for Win2k clients, but not Win9X at present.  NTLMSSP updates
   are needed.
 - Now uses fgets(), not x_fgets() to cope with Squid environment (I think
   somthing to do with non-blocking stdin).

- Add much more robust connection code to wb_common.c - it will not connect to
  a server of a different protocol version, and it will automatically try and
  reconnect to the 'privileged' pipe if possible.
  - This could help with 'privileged' idmap operations etc in future.

- Add a generic HEX encode routine to util_str.c,
- fix a small line of dodgy C in StrnCpy_fn()

- Correctly pull our 'session key' out of the info3 from th the DC.  This is
  used in both the auth code, and in for export over the winbind pipe to
  ntlm_auth.

- Given the user's challenge/response and access to the privileged pipe,
  allow external access to the 'session key'.  To be used for MSCHAPv2
  integration.

Andrew Bartlett

14 files changed:
source/auth/auth_domain.c
source/auth/auth_util.c
source/include/rpc_netlogon.h
source/lib/util_str.c
source/libsmb/ntlmssp.c
source/nsswitch/wb_common.c
source/nsswitch/winbindd.c
source/nsswitch/winbindd.h
source/nsswitch/winbindd_misc.c
source/nsswitch/winbindd_nss.h
source/nsswitch/winbindd_pam.c
source/nsswitch/winbindd_util.c
source/rpc_client/cli_netlogon.c
source/utils/ntlm_auth.c

index 079bb49..7cf7ed1 100644 (file)
@@ -350,13 +350,6 @@ static NTSTATUS domain_client_validate(TALLOC_CTX *mem_ctx,
        } else {
                nt_status = make_server_info_info3(mem_ctx, user_info->internal_username.str, 
                                                   user_info->smb_name.str, domain, server_info, &info3);
-#if 0 
-               /* The stuff doesn't work right yet */
-               SMB_ASSERT(sizeof((*server_info)->session_key) == sizeof(info3.user_sess_key)); 
-               memcpy((*server_info)->session_key, info3.user_sess_key, sizeof((*server_info)->session_key)/* 16 */);
-               SamOEMhash((*server_info)->session_key, trust_passwd, sizeof((*server_info)->session_key));
-#endif         
-
                uni_group_cache_store_netlogon(mem_ctx, &info3);
        }
 
index f4c43d2..70accec 100644 (file)
@@ -1066,6 +1066,9 @@ NTSTATUS make_server_info_info3(TALLOC_CTX *mem_ctx,
 
        SAFE_FREE(all_group_SIDs);
        
+       memcpy((*server_info)->session_key, info3.user_sess_key, sizeof((*server_info)->session_key)/* 16 */);
+       memcpy((*server_info)->first_8_lm_hash, info3.padding, 8);
+
        return NT_STATUS_OK;
 }
 
index fb849f8..74e3a50 100644 (file)
@@ -156,7 +156,7 @@ typedef struct net_user_info_3
        uint32 buffer_groups; /* undocumented buffer pointer to groups. */
        uint32 user_flgs;     /* user flags */
 
-       uint8 user_sess_key[16]; /* unused user session key */
+       uint8 user_sess_key[16]; /* user session key */
 
        UNIHDR hdr_logon_srv; /* logon server unicode string header */
        UNIHDR hdr_logon_dom; /* logon domain unicode string header */
index d1e57ed..4d955c5 100644 (file)
@@ -603,8 +603,12 @@ char *StrnCpy_fn(const char *fn, int line,char *dest,const char *src,size_t n)
                *dest = 0;
                return(dest);
        }
-       while (n-- && (*d++ = *src++))
-               ;
+       
+       while (n-- && (*d = *src)) {
+               d++;
+               src++;
+       }
+
        *d = 0;
        return(dest);
 }
@@ -681,6 +685,22 @@ size_t strhex_to_str(char *p, size_t len, const char *strhex)
        return num_chars;
 }
 
+/**
+ * Routine to print a buffer as HEX digits, into an allocated string.
+ */
+
+void hex_encode(const unsigned char *buff_in, size_t len, char **out_hex_buffer)
+{
+       int i;
+       char *hex_buffer;
+
+       *out_hex_buffer = smb_xmalloc((len*2)+1);
+       hex_buffer = *out_hex_buffer;
+
+       for (i = 0; i < len; i++)
+               slprintf(&hex_buffer[i*2], 3, "%02X", buff_in[i]);
+}
+
 /**
  Check if a string is part of a list.
 **/
index 5722b8e..0cd1ac3 100644 (file)
@@ -121,7 +121,8 @@ static NTSTATUS ntlmssp_server_negotiate(struct ntlmssp_state *ntlmssp_state,
 {
        DATA_BLOB struct_blob;
        fstring dnsname, dnsdomname;
-       uint32 ntlmssp_command, neg_flags, chal_flags;
+       uint32 neg_flags = 0;
+       uint32 ntlmssp_command, chal_flags;
        char *cliname=NULL, *domname=NULL;
        const uint8 *cryptkey;
        const char *target_name;
@@ -131,20 +132,24 @@ static NTSTATUS ntlmssp_server_negotiate(struct ntlmssp_state *ntlmssp_state,
        file_save("ntlmssp_negotiate.dat", request.data, request.length);
 #endif
 
-       if (!msrpc_parse(&request, "CddAA",
-                        "NTLMSSP",
-                        &ntlmssp_command,
-                        &neg_flags,
-                        &cliname,
-                        &domname)) {
-               return NT_STATUS_INVALID_PARAMETER;
+       if (request.length) {
+               if (!msrpc_parse(&request, "CddAA",
+                                "NTLMSSP",
+                                &ntlmssp_command,
+                                &neg_flags,
+                                &cliname,
+                                &domname)) {
+                       DEBUG(1, ("ntlmssp_server_negotiate: failed to parse NTLMSSP:\n"));
+                       dump_data(2, request.data, request.length);
+                       return NT_STATUS_INVALID_PARAMETER;
+               }
+               
+               SAFE_FREE(cliname);
+               SAFE_FREE(domname);
+               
+               debug_ntlmssp_flags(neg_flags);
        }
-
-       SAFE_FREE(cliname);
-       SAFE_FREE(domname);
-  
-       debug_ntlmssp_flags(neg_flags);
-
+       
        cryptkey = ntlmssp_state->get_challenge(ntlmssp_state);
 
        data_blob_free(&ntlmssp_state->chal);
@@ -268,6 +273,8 @@ static NTSTATUS ntlmssp_server_auth(struct ntlmssp_state *ntlmssp_state,
                         &ntlmssp_state->workstation,
                         &sess_key,
                         &neg_flags)) {
+               DEBUG(1, ("ntlmssp_server_auth: failed to parse NTLMSSP:\n"));
+               dump_data(2, request.data, request.length);
                return NT_STATUS_INVALID_PARAMETER;
        }
 
@@ -357,13 +364,19 @@ NTSTATUS ntlmssp_server_update(NTLMSSP_STATE *ntlmssp_state,
        uint32 ntlmssp_command;
        *reply = data_blob(NULL, 0);
 
-       if (!msrpc_parse(&request, "Cd",
-                        "NTLMSSP",
-                        &ntlmssp_command)) {
-               return NT_STATUS_INVALID_PARAMETER;
+       if (request.length) {
+               if (!msrpc_parse(&request, "Cd",
+                                "NTLMSSP",
+                                &ntlmssp_command)) {
+                       return NT_STATUS_INVALID_PARAMETER;
+               }
+       } else {
+               /* 'datagram' mode - no neg packet */
+               ntlmssp_command = NTLMSSP_NEGOTIATE;
        }
 
        if (ntlmssp_command != ntlmssp_state->expected_state) {
+               DEBUG(1, ("got NTLMSSP command %u, expected %u\n", ntlmssp_command, ntlmssp_state->expected_state));
                return NT_STATUS_INVALID_PARAMETER;
        }
 
@@ -372,6 +385,7 @@ NTSTATUS ntlmssp_server_update(NTLMSSP_STATE *ntlmssp_state,
        } else if (ntlmssp_command == NTLMSSP_AUTH) {
                return ntlmssp_server_auth(ntlmssp_state, request, reply);
        } else {
+               DEBUG(1, ("unknown NTLMSSP command %u\n", ntlmssp_command, ntlmssp_state->expected_state));
                return NT_STATUS_INVALID_PARAMETER;
        }
 }
index 89c751a..ac1ccb2 100644 (file)
@@ -131,27 +131,16 @@ static int make_safe_fd(int fd)
 
 /* Connect to winbindd socket */
 
-int winbind_open_pipe_sock(void)
+static int winbind_named_pipe_sock(const char *dir)
 {
-#ifdef HAVE_UNIXSOCKET
        struct sockaddr_un sunaddr;
-       static pid_t our_pid;
        struct stat st;
        pstring path;
        int fd;
        
-       if (our_pid != getpid()) {
-               close_sock();
-               our_pid = getpid();
-       }
-       
-       if (winbindd_fd != -1) {
-               return winbindd_fd;
-       }
-       
        /* Check permissions on unix socket directory */
        
-       if (lstat(WINBINDD_SOCKET_DIR, &st) == -1) {
+       if (lstat(dir, &st) == -1) {
                return -1;
        }
        
@@ -162,13 +151,13 @@ int winbind_open_pipe_sock(void)
        
        /* Connect to socket */
        
-       strncpy(path, WINBINDD_SOCKET_DIR, sizeof(path) - 1);
+       strncpy(path, dir, sizeof(path) - 1);
        path[sizeof(path) - 1] = '\0';
        
-       strncat(path, "/", sizeof(path) - 1);
+       strncat(path, "/", sizeof(path) - 1 - strlen(path));
        path[sizeof(path) - 1] = '\0';
        
-       strncat(path, WINBINDD_SOCKET_NAME, sizeof(path) - 1);
+       strncat(path, WINBINDD_SOCKET_NAME, sizeof(path) - 1 - strlen(path));
        path[sizeof(path) - 1] = '\0';
        
        ZERO_STRUCT(sunaddr);
@@ -196,16 +185,60 @@ int winbind_open_pipe_sock(void)
                return -1;
        }
 
-       if ((winbindd_fd = make_safe_fd( fd)) == -1) {
-               return winbindd_fd;
+       if ((fd = make_safe_fd( fd)) == -1) {
+               return fd;
        }
        
-       if (connect(winbindd_fd, (struct sockaddr *)&sunaddr, 
+       if (connect(fd, (struct sockaddr *)&sunaddr, 
                    sizeof(sunaddr)) == -1) {
-               close_sock();
+               close(fd);
                return -1;
        }
         
+       return fd;
+}
+
+/* Connect to winbindd socket */
+
+int winbind_open_pipe_sock(void)
+{
+#ifdef HAVE_UNIXSOCKET
+       static pid_t our_pid;
+       struct winbindd_request request;
+       struct winbindd_response response;
+       ZERO_STRUCT(request);
+       ZERO_STRUCT(response);
+
+       if (our_pid != getpid()) {
+               close_sock();
+               our_pid = getpid();
+       }
+       
+       if (winbindd_fd != -1) {
+               return winbindd_fd;
+       }
+
+       if ((winbindd_fd = winbind_named_pipe_sock(WINBINDD_SOCKET_DIR)) == -1) {
+               return -1;
+       }
+
+       /* version-check the socket */
+
+       if ((winbindd_request(WINBINDD_INTERFACE_VERSION, &request, &response) != NSS_STATUS_SUCCESS) || (response.data.interface_version != WINBIND_INTERFACE_VERSION)) {
+               close_sock();
+               return -1;
+       }
+
+       /* try and get priv pipe */
+
+       if (winbindd_request(WINBINDD_PRIV_PIPE_DIR, &request, &response) == NSS_STATUS_SUCCESS) {
+               int fd;
+               if ((fd = winbind_named_pipe_sock(response.extra_data)) != -1) {
+                       close(winbindd_fd);
+                       winbindd_fd = fd;
+               }
+       }
+
        return winbindd_fd;
 #else
        return -1;
index 921f7d2..4033952 100644 (file)
@@ -259,6 +259,7 @@ static struct dispatch_table dispatch_table[] = {
        { WINBINDD_INTERFACE_VERSION, winbindd_interface_version, "INTERFACE_VERSION" },
        { WINBINDD_DOMAIN_NAME, winbindd_domain_name, "DOMAIN_NAME" },
        { WINBINDD_NETBIOS_NAME, winbindd_netbios_name, "NETBIOS_NAME" },
+       { WINBINDD_PRIV_PIPE_DIR, winbindd_priv_pipe_dir, "WINBINDD_PRIV_PIPE_DIR" },
 
        /* WINS functions */
 
@@ -305,7 +306,7 @@ static void process_request(struct winbindd_cli_state *state)
 
 /* Process a new connection by adding it to the client connection list */
 
-static void new_connection(int listen_sock)
+static void new_connection(int listen_sock, BOOL privilaged)
 {
        struct sockaddr_un sunaddr;
        struct winbindd_cli_state *state;
@@ -336,6 +337,8 @@ static void new_connection(int listen_sock)
 
        state->last_access = time(NULL);        
 
+       state->privilaged = privilaged;
+
        /* Add to connection list */
        
        winbindd_add_client(state);
@@ -547,7 +550,7 @@ static void process_loop(void)
        while (1) {
                struct winbindd_cli_state *state;
                fd_set r_fds, w_fds;
-               int maxfd, listen_sock, selret;
+               int maxfd, listen_sock, listen_priv_sock, selret;
                struct timeval timeout;
 
                /* Handle messages */
@@ -566,17 +569,19 @@ static void process_loop(void)
                /* Initialise fd lists for select() */
 
                listen_sock = open_winbindd_socket();
+               listen_priv_sock = open_winbindd_priv_socket();
 
-               if (listen_sock == -1) {
+               if (listen_sock == -1 || listen_priv_sock == -1) {
                        perror("open_winbind_socket");
                        exit(1);
                }
 
-               maxfd = listen_sock;
+               maxfd = MAX(listen_sock, listen_priv_sock);
 
                FD_ZERO(&r_fds);
                FD_ZERO(&w_fds);
                FD_SET(listen_sock, &r_fds);
+               FD_SET(listen_priv_sock, &r_fds);
 
                timeout.tv_sec = WINBINDD_ESTABLISH_LOOP;
                timeout.tv_usec = 0;
@@ -653,7 +658,22 @@ static void process_loop(void)
                                                break;
                                        }
                                }
-                               new_connection(listen_sock);
+                               /* new, non-privilaged connection */
+                               new_connection(listen_sock, False);
+                       }
+            
+                       if (FD_ISSET(listen_priv_sock, &r_fds)) {
+                               while (winbindd_num_clients() > WINBINDD_MAX_SIMULTANEOUS_CLIENTS - 1) {
+                                       DEBUG(5,("winbindd: Exceeding %d client connections, removing idle connection.\n",
+                                               WINBINDD_MAX_SIMULTANEOUS_CLIENTS));
+                                       if (!remove_idle_client()) {
+                                               DEBUG(0,("winbindd: Exceeding %d client connections, no idle connection found\n",
+                                                       WINBINDD_MAX_SIMULTANEOUS_CLIENTS));
+                                               break;
+                                       }
+                               }
+                               /* new, privilaged connection */
+                               new_connection(listen_priv_sock, True);
                        }
             
                        /* Process activity on client connections */
index 42ef209..d98ac28 100644 (file)
@@ -43,6 +43,8 @@ struct winbindd_cli_state {
        BOOL finished;                            /* Can delete from list */
        BOOL write_extra_data;                    /* Write extra_data field */
        time_t last_access;                       /* Time of last access (read or write) */
+       BOOL privilaged;                           /* Is the client 'privilaged' */
+
        struct winbindd_request request;          /* Request from client */
        struct winbindd_response response;        /* Respose to client */
        struct getent_state *getpwent_state;      /* State for getpwent() */
index 0b28381..3b44d02 100644 (file)
@@ -233,3 +233,20 @@ enum winbindd_result winbindd_netbios_name(struct winbindd_cli_state *state)
 
        return WINBINDD_OK;
 }
+
+/* What's my name again? */
+
+enum winbindd_result winbindd_priv_pipe_dir(struct winbindd_cli_state *state)
+{
+
+       DEBUG(3, ("[%5d]: request location of privilaged pipe\n", state->pid));
+       
+       state->response.extra_data = strdup(get_winbind_priv_pipe_dir());
+       if (!state->response.extra_data)
+               return WINBINDD_ERROR;
+
+       /* must add one to length to copy the 0 for string termination */
+       state->response.length += strlen((char *)state->response.extra_data) + 1;
+
+       return WINBINDD_OK;
+}
index 2c87a77..88f4a11 100644 (file)
@@ -30,7 +30,7 @@
 
 #define WINBINDD_SOCKET_NAME "pipe"            /* Name of PF_UNIX socket */
 #define WINBINDD_SOCKET_DIR  "/tmp/.winbindd"  /* Name of PF_UNIX dir */
-
+#define WINBINDD_PRIV_SOCKET_SUBDIR "winbindd_privilaged" /* name of subdirectory of lp_lockdir() to hold the 'privilaged' pipe */
 #define WINBINDD_DOMAIN_ENV  "WINBINDD_DOMAIN" /* Environment variables */
 #define WINBINDD_DONT_ENV    "_NO_WINBINDD"
 
@@ -105,6 +105,9 @@ enum winbindd_cmd {
        WINBINDD_NETBIOS_NAME,       /* The netbios name of the server */
        /* Placeholder for end of cmd list */
 
+       /* find the location of our privilaged pipe */
+       WINBINDD_PRIV_PIPE_DIR,
+
        WINBINDD_NUM_CMDS
 };
 
index e24afba..d408a8b 100644 (file)
@@ -174,6 +174,12 @@ enum winbindd_result winbindd_pam_auth_crap(struct winbindd_cli_state *state)
 
        DATA_BLOB lm_resp, nt_resp;
 
+       if (!state->privilaged) {
+               DEBUG(2, ("winbindd_pam_auth_crap: non-privilaged access denied!\n"));
+               result =  NT_STATUS_ACCESS_DENIED;
+               goto done;
+       }
+
        /* Ensure null termination */
        state->request.data.auth_crap.user[sizeof(state->request.data.auth_crap.user)-1]='\0';
 
@@ -272,19 +278,12 @@ enum winbindd_result winbindd_pam_auth_crap(struct winbindd_cli_state *state)
                        result = append_info3_as_ndr(mem_ctx, state, &info3);
                }
 
-#if 0
-               /* we don't currently do this stuff right */
-               /* Doing an assert in a daemon is going to be a pretty bad 
-                   idea. - tpot */
                if (state->request.data.auth_crap.flags & WINBIND_PAM_NTKEY) {
-                       SMB_ASSERT(sizeof(state->response.data.auth.nt_session_key) == sizeof(info3.user_sess_key)); 
                        memcpy(state->response.data.auth.nt_session_key, info3.user_sess_key, sizeof(state->response.data.auth.nt_session_key) /* 16 */);
                }
                if (state->request.data.auth_crap.flags & WINBIND_PAM_LMKEY) {
-                       SMB_ASSERT(sizeof(state->response.data.auth.nt_session_key) <= sizeof(info3.user_sess_key)); 
                        memcpy(state->response.data.auth.first_8_lm_hash, info3.padding, sizeof(state->response.data.auth.nt_session_key) /* 16 */);
                }
-#endif
        }
 
 done:
index fdbfd92..b033380 100644 (file)
@@ -453,9 +453,15 @@ void fill_domain_username(fstring name, const char *domain, const char *user)
  * Winbindd socket accessor functions
  */
 
+char *get_winbind_priv_pipe_dir(void) 
+{
+       return lock_path(WINBINDD_PRIV_SOCKET_SUBDIR);
+}
+
 /* Open the winbindd socket */
 
 static int _winbindd_socket = -1;
+static int _winbindd_priv_socket = -1;
 
 int open_winbindd_socket(void)
 {
@@ -469,6 +475,18 @@ int open_winbindd_socket(void)
        return _winbindd_socket;
 }
 
+int open_winbindd_priv_socket(void)
+{
+       if (_winbindd_priv_socket == -1) {
+               _winbindd_priv_socket = create_pipe_sock(
+                       get_winbind_priv_pipe_dir(), WINBINDD_SOCKET_NAME, 0750);
+               DEBUG(10, ("open_winbindd_priv_socket: opened socket fd %d\n",
+                          _winbindd_priv_socket));
+       }
+
+       return _winbindd_priv_socket;
+}
+
 /* Close the winbindd socket */
 
 void close_winbindd_socket(void)
@@ -479,6 +497,12 @@ void close_winbindd_socket(void)
                close(_winbindd_socket);
                _winbindd_socket = -1;
        }
+       if (_winbindd_priv_socket != -1) {
+               DEBUG(10, ("close_winbindd_socket: closing socket fd %d\n",
+                          _winbindd_priv_socket));
+               close(_winbindd_priv_socket);
+               _winbindd_priv_socket = -1;
+       }
 }
 
 /*
index cbb0980..f83571a 100644 (file)
@@ -597,7 +597,7 @@ NTSTATUS cli_netlogon_sam_logon(struct cli_state *cli, TALLOC_CTX *mem_ctx,
 
 NTSTATUS cli_netlogon_sam_network_logon(struct cli_state *cli, TALLOC_CTX *mem_ctx,
                                        const char *username, const char *domain, const char *workstation, 
-                                       const uint8 chal[8],
+                                       const uint8 chal[8], 
                                        DATA_BLOB lm_response, DATA_BLOB nt_response,
                                        NET_USER_INFO_3 *info3)
 
@@ -610,6 +610,8 @@ NTSTATUS cli_netlogon_sam_network_logon(struct cli_state *cli, TALLOC_CTX *mem_c
        NET_ID_INFO_CTR ctr;
        int validation_level = 3;
        char *workstation_name_slash;
+       uint8 netlogon_sess_key[16];
+       static uint8 zeros[16];
        
        ZERO_STRUCT(q);
        ZERO_STRUCT(r);
@@ -662,6 +664,15 @@ NTSTATUS cli_netlogon_sam_network_logon(struct cli_state *cli, TALLOC_CTX *mem_c
                goto done;
        }
 
+       ZERO_STRUCT(netlogon_sess_key);
+       memcpy(netlogon_sess_key, cli->sess_key, 8);
+       
+       if (memcmp(zeros, info3->user_sess_key, 16) != 0)
+               SamOEMhash(info3->user_sess_key, netlogon_sess_key, 16);
+               
+       if (memcmp(zeros, info3->padding, 16) != 0)
+               SamOEMhash(info3->padding, netlogon_sess_key, 16);
+
         /* Return results */
 
        result = r.status;
index b76308c..ac45676 100644 (file)
@@ -51,6 +51,8 @@ static unsigned char *lm_response;
 static size_t lm_response_len;
 static unsigned char *nt_response;
 static size_t nt_response_len;
+static int request_lm_key;
+static int request_nt_key;
 
 static char *password;
 
@@ -197,7 +199,7 @@ static NTSTATUS winbind_pw_check(struct ntlmssp_state *ntlmssp_state)
        memcpy(request.data.auth_crap.lm_resp, ntlmssp_state->lm_resp.data, 
               MIN(ntlmssp_state->lm_resp.length, sizeof(request.data.auth_crap.lm_resp)));
         
-       memcpy(request.data.auth_crap.nt_resp, ntlmssp_state->lm_resp.data,
+       memcpy(request.data.auth_crap.nt_resp, ntlmssp_state->nt_resp.data,
               MIN(ntlmssp_state->nt_resp.length, sizeof(request.data.auth_crap.nt_resp)));
         
         request.data.auth_crap.lm_resp_len = ntlmssp_state->lm_resp.length;
@@ -217,10 +219,28 @@ static NTSTATUS winbind_pw_check(struct ntlmssp_state *ntlmssp_state)
 static void manage_squid_ntlmssp_request(enum squid_mode squid_mode, 
                                         char *buf, int length) 
 {
-       static NTLMSSP_STATE *ntlmssp_state;
+       static NTLMSSP_STATE *ntlmssp_state = NULL;
        DATA_BLOB request, reply;
        NTSTATUS nt_status;
 
+       if (strlen(buf) < 2) {
+               DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
+               x_fprintf(x_stdout, "BH\n");
+               return;
+       }
+
+       if (strlen(buf) > 3) {
+               request = base64_decode_data_blob(buf + 3);
+       } else if (strcmp(buf, "YR") == 0) {
+               request = data_blob(NULL, 0);
+               if (ntlmssp_state)
+                       ntlmssp_server_end(&ntlmssp_state);
+       } else {
+               DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
+               x_fprintf(x_stdout, "BH\n");
+               return;
+       }
+
        if (!ntlmssp_state) {
                ntlmssp_server_start(&ntlmssp_state);
                ntlmssp_state->check_password = winbind_pw_check;
@@ -228,15 +248,8 @@ static void manage_squid_ntlmssp_request(enum squid_mode squid_mode,
                ntlmssp_state->get_global_myname = get_winbind_netbios_name;
        }
 
-       if (strlen(buf) < 3) {
-               x_fprintf(x_stdout, "BH\n");
-               return;
-       }
-       
-       request = base64_decode_data_blob(buf + 3);
-       
-       DEBUG(0, ("got NTLMSSP packet:\n"));
-       dump_data(0, request.data, request.length);
+       DEBUG(10, ("got NTLMSSP packet:\n"));
+       dump_data(10, request.data, request.length);
 
        nt_status = ntlmssp_server_update(ntlmssp_state, request, &reply);
        
@@ -245,10 +258,13 @@ static void manage_squid_ntlmssp_request(enum squid_mode squid_mode,
                x_fprintf(x_stdout, "TT %s\n", reply_base64);
                SAFE_FREE(reply_base64);
                data_blob_free(&reply);
+               DEBUG(10, ("NTLMSSP challenge\n"));
        } else if (!NT_STATUS_IS_OK(nt_status)) {
                x_fprintf(x_stdout, "NA %s\n", nt_errstr(nt_status));
+               DEBUG(10, ("NTLMSSP %s\n", nt_errstr(nt_status)));
        } else {
                x_fprintf(x_stdout, "AF %s\\%s\n", ntlmssp_state->domain, ntlmssp_state->user);
+               DEBUG(10, ("NTLMSSP OK!\n"));
        }
 
        data_blob_free(&request);
@@ -287,10 +303,11 @@ static void manage_squid_request(enum squid_mode squid_mode)
        int length;
        char *c;
        static BOOL err;
-  
-       if (x_fgets(buf, sizeof(buf)-1, x_stdin) == NULL) {
-               DEBUG(1, ("fgets() failed! dying..... errno=%d (%s)\n", errno,
-                         strerror(errno)));
+
+       /* this is not a typo - x_fgets doesn't work too well under squid */
+       if (fgets(buf, sizeof(buf)-1, stdin) == NULL) {
+               DEBUG(1, ("fgets() failed! dying..... errno=%d (%s)\n", ferror(stdin),
+                         strerror(ferror(stdin))));
                exit(1);    /* BIIG buffer */
        }
     
@@ -341,12 +358,22 @@ static BOOL check_auth_crap(void)
 {
        struct winbindd_request request;
        struct winbindd_response response;
+       char *lm_key;
+       char *nt_key;
+       static uint8 zeros[16];
+
         NSS_STATUS result;
        /* Send off request */
 
        ZERO_STRUCT(request);
        ZERO_STRUCT(response);
 
+       if (request_lm_key) 
+               request.data.auth_crap.flags |= WINBIND_PAM_LMKEY;
+
+       if (request_nt_key) 
+               request.data.auth_crap.flags |= WINBIND_PAM_NTKEY;
+
        fstrcpy(request.data.auth_crap.user, username);
 
        fstrcpy(request.data.auth_crap.domain, domain);
@@ -373,6 +400,27 @@ static BOOL check_auth_crap(void)
                 response.data.auth.nt_status_string, 
                 response.data.auth.nt_status);
 
+       if (response.data.auth.nt_status == 0) {
+               if (request_lm_key 
+                   && (memcmp(zeros, response.data.auth.first_8_lm_hash, 
+                             sizeof(response.data.auth.first_8_lm_hash)) != 0)) {
+                       hex_encode(response.data.auth.first_8_lm_hash, 
+                                  sizeof(response.data.auth.first_8_lm_hash),
+                                  &lm_key);
+                       d_printf("LM_KEY: %s\n", lm_key);
+                       SAFE_FREE(lm_key);
+               }
+               if (request_nt_key 
+                   && (memcmp(zeros, response.data.auth.nt_session_key, 
+                             sizeof(response.data.auth.nt_session_key)) != 0)) {
+                       hex_encode(response.data.auth.nt_session_key, 
+                                  sizeof(response.data.auth.nt_session_key), 
+                                  &nt_key);
+                       d_printf("NT_KEY: %s\n", nt_key);
+                       SAFE_FREE(nt_key);
+               }
+       }
+
         return result == NSS_STATUS_SUCCESS;
 }
 
@@ -386,68 +434,11 @@ enum {
        OPT_RESPONSE,
        OPT_LM,
        OPT_NT,
-       OPT_PASSWORD
+       OPT_PASSWORD,
+       OPT_LM_KEY,
+       OPT_NT_KEY
 };
 
-/*************************************************************
- Routine to set hex password characters into an allocated array.
-**************************************************************/
-
-static void hex_encode(const unsigned char *buff_in, size_t len, char **out_hex_buffer)
-{
-       int i;
-       char *hex_buffer;
-
-       *out_hex_buffer = smb_xmalloc((len*2)+1);
-       hex_buffer = *out_hex_buffer;
-
-       for (i = 0; i < len; i++)
-               slprintf(&hex_buffer[i*2], 3, "%02X", buff_in[i]);
-}
-
-/*************************************************************
- Routine to get the 32 hex characters and turn them
- into a 16 byte array.
-**************************************************************/
-
-static BOOL hex_decode(const char *hex_buf_in, unsigned char **out_buffer, size_t *size)
-{
-       int i;
-       size_t hex_buf_in_len = strlen(hex_buf_in);
-       unsigned char  partial_byte_hex;
-       unsigned char  partial_byte;
-       const char     *hexchars = "0123456789ABCDEF";
-       char           *p;
-       BOOL           high = True;
-       
-       if (!hex_buf_in) 
-               return (False);
-       
-       *size = (hex_buf_in_len + 1) / 2;
-
-       *out_buffer = smb_xmalloc(*size);
-       
-       for (i = 0; i < hex_buf_in_len; i++) {
-               partial_byte_hex = toupper(hex_buf_in[i]);
-
-               p = strchr(hexchars, partial_byte_hex);
-
-               if (!p)
-                       return (False);
-
-               partial_byte = PTR_DIFF(p, hexchars);
-
-               if (high) {
-                       (*out_buffer)[i / 2] = (partial_byte << 4);
-               } else {
-                       (*out_buffer)[i / 2] |= partial_byte;
-               }
-               high = !high;
-       }
-       return (True);
-}
-
-
 int main(int argc, const char **argv)
 {
        int opt;
@@ -464,6 +455,8 @@ int main(int argc, const char **argv)
                { "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, &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_nt_key, OPT_NT_KEY, "Retreive NT session key"},
                { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_debug },
                { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_configfile },
                { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_version},
@@ -491,20 +484,27 @@ int main(int argc, const char **argv)
        while((opt = poptGetNextOpt(pc)) != -1) {
                switch (opt) {
                case OPT_CHALLENGE:
-                       if (!hex_decode(hex_challenge, &challenge, &challenge_len)) {
-                               fprintf(stderr, "hex decode of %s failed!\n", hex_challenge);
+                       challenge_len = strlen(hex_challenge);
+                       challenge = smb_xmalloc((challenge_len+1)/2);
+                       if ((challenge_len = strhex_to_str(challenge, challenge_len, hex_challenge)) != 8) {
+                               fprintf(stderr, "hex decode of %s failed (only got %u bytes)!\n", 
+                                       hex_challenge, challenge_len);
                                exit(1);
                        }
                        break;
                case OPT_LM: 
-                       if (!hex_decode(hex_lm_response, &lm_response, &lm_response_len)) {
-                               fprintf(stderr, "hex decode of %s failed!\n", lm_response);
+                       lm_response_len = strlen(hex_lm_response);
+                       lm_response = smb_xmalloc((lm_response_len+1)/2);
+                       if ((lm_response_len = strhex_to_str(lm_response, lm_response_len, hex_lm_response)) != 24) {
+                               fprintf(stderr, "hex decode of %s failed!\n", hex_lm_response);
                                exit(1);
                        }
                        break;
-               case OPT_NT:
-                       if (!hex_decode(hex_lm_response, &lm_response, &lm_response_len)) {
-                               fprintf(stderr, "hex decode of %s failed!\n", lm_response);
+               case OPT_NT: 
+                       nt_response_len = strlen(hex_nt_response);
+                       nt_response = smb_xmalloc((nt_response_len+1)/2);
+                       if ((nt_response_len = strhex_to_str(nt_response, nt_response_len, hex_nt_response)) < 24) {
+                               fprintf(stderr, "hex decode of %s failed!\n", hex_nt_response);
                                exit(1);
                        }
                        break;