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
(This used to be commit dcdc75ebd89f504a0f6e3a3bc5b43298858d276b)

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

index 079bb49a21d5dc03a73a404800f08d3f4b83e895..7cf7ed119992f05d9141420f539edbd57be8654e 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 f4c43d2d4bb3e97e8d2498eb12e54c123633a5d0..70accec40676e16e5558194dae0afa5bb2e16815 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 fb849f82380600154408d72c247e5c581ad61e60..74e3a50ee4a64c345cbde9eca192d0d0df9d61a6 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 d1e57ed5cfe6eca1e0f29b290ad51ae22074517f..4d955c59a70ef422ef469947304b056a648df5c8 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 5722b8efcdaced108cdccde589aaa0c07f7b4e45..0cd1ac33ec2ec4297c5f22920ca3b9bfec075fb7 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 89c751a4efb17ff5416fd5fa422f4910455bd23d..ac1ccb217ed71aac3d767a7241b40476e8a72b5d 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 921f7d28642cdcad794276840e8165f97cc1d7ea..4033952a6ed615b066ade3b25515949d9197c869 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 42ef209fafa972917e5a3e83782eacf183cc1591..d98ac28ab14036f887009932f48ac21ea426c4a0 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 0b283812b2198fa6543ff33fd729af6d20c7397a..3b44d029c004083a7206f25fcfa33652d6e05f60 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 2c87a771009a8d93bd8e8019d517c964d5d6aafc..88f4a11f87553eeabb5b55424e76824dece6c5b5 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 e24afbabd601a0600c7ba3c036d556d0b08e25ec..d408a8b3ae4a2da9ef79b4695c1a9d3c0cf70e54 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 fdbfd92b5a7e92143a0495b221b1e30703ab54b4..b033380206a39f264d49c337175dbcfb3b83edd4 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 cbb09803af6af9208be4cbafe136b3fcdd225113..f83571af034c87fbe0469e31cb21e7fc7ae0cd88 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 b76308c55fb0103edfc6bbfa181004510c015870..ac456769f22d0021430af1ecb377fd0d019ee64d 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;