Hello Joe,
I tested your patch and it didn't work (as reported before). Therefore I
cleaned up my own hack. With this patch (on my system at least) the reported
problems do not occur. I based my patch on 0.26.3. The patch, in diff -c
format, follows at the end of this mail.
My tests consists of:
svn lock
svn unlock --force
where my apache uses SSPI/NTLM, with maxkeepalives = 2

This handles the 2 scenarios described in my original bug-report. With my
patch they succeed without problems (afaict)

Hope to help,
Robert van der Boon

On 2/5/07, Robert van der Boon <[EMAIL PROTECTED]> wrote:

>> I tracked the problem down to this:
>> 1. neon doesnot clear its sspi context / token if a connection is
>> closed. Therefore the old token is used on the new connection, and the
>> server does not accept that.

2. neon should not close the connection if it is in the sspi/auth
>> negotiation for the non-idempotent messages (line 1216 in
>> ne_request.c). The NTLM authentication is a multi-leg authentication,
>> meaning we need at least 2 requests to do authentication. neon closes
>> the connection after the first request, resulting in problem 1..

In my patch I introduced a connection_closed hook and a
new "please do not disconnect, I'm in auth negotiation"-flag.


My patch is:
###############################################
diff -cr neon-0.26.3\src\ne_auth.c neon-0.26.3.rjvdboon\src\ne_auth.c
*** neon-0.26.3\src\ne_auth.c    Mon Jan 22 17:12:24 2007
--- neon-0.26.3.rjvdboon\src\ne_auth.c    Thu Feb 15 12:58:13 2007
***************
*** 238,243 ****
--- 238,253 ----
     int flags; /* AUTH_FLAG_* flags */
 };

+ #ifdef HAVE_SSPI
+ static void clean_session_sspi(auth_session *sess)
+ {
+     if (sess->sspi_token) ne_free(sess->sspi_token);
+     sess->sspi_token = NULL;
+     ne_sspi_destroy_context(sess->sspi_context);
+     sess->sspi_context = NULL;
+ }
+ #endif
+
 static void clean_session(auth_session *sess)
 {
     if (sess->basic) ne_free(sess->basic);
***************
*** 263,272 ****
     sess->gssapi_token = NULL;
 #endif
 #ifdef HAVE_SSPI
!     if (sess->sspi_token) ne_free(sess->sspi_token);
!     sess->sspi_token = NULL;
!     ne_sspi_destroy_context(sess->sspi_context);
!     sess->sspi_context = NULL;
 #endif
 }

--- 273,279 ----
     sess->gssapi_token = NULL;
 #endif
 #ifdef HAVE_SSPI
!     clean_session_sspi(sess);
 #endif
 }

***************
*** 551,557 ****
 #ifdef HAVE_SSPI
 static char *request_sspi(auth_session *sess, struct auth_request
*request)
 {
!     return ne_concat(sess->protocol->name, " ", sess->sspi_token, "\r\n",
NULL);
 }

 static int sspi_challenge(auth_session *sess, int attempt,
--- 558,566 ----
 #ifdef HAVE_SSPI
 static char *request_sspi(auth_session *sess, struct auth_request
*request)
 {
!     if (sess->sspi_token == NULL)
!         return NULL;
!     return ne_concat(sess->protocol->name, " ", sess->sspi_token, "\r\n",
NULL);
 }

 static int sspi_challenge(auth_session *sess, int attempt,
***************
*** 1211,1217 ****
         && (sess->protocol->flags & AUTH_FLAG_VERIFY_NON40x) == 0) {
         ret = sess->protocol->verify(areq, sess, auth_info_hdr);
     }
!     else if (sess->protocol
              && sess->protocol->flags && AUTH_FLAG_VERIFY_NON40x
              && (status->klass == 2 || status->klass == 3)
              && auth_hdr) {
--- 1220,1226 ----
         && (sess->protocol->flags & AUTH_FLAG_VERIFY_NON40x) == 0) {
         ret = sess->protocol->verify(areq, sess, auth_info_hdr);
     }
!     else if (sess->protocol && sess->protocol->verify
              && sess->protocol->flags && AUTH_FLAG_VERIFY_NON40x
              && (status->klass == 2 || status->klass == 3)
              && auth_hdr) {
***************
*** 1234,1239 ****
--- 1243,1251 ----
     else if (sess->sspi_context) {
         ne_sspi_clear_context(sess->sspi_context);
     }
+     if (sess->protocol && sess->protocol->id == NE_AUTH_NEGOTIATE &&
sess->sspi_context) {
+         ne_set_request_flag(req, NE_REQFLAG_IN_AUTH_NEGOTIATION,
ne_sspi_is_in_negotiation(sess->sspi_context));
+     }
 #endif

     return ret;
***************
*** 1269,1274 ****
--- 1281,1292 ----
     clean_session(sess);
     ne_free(sess);
 }
+ static void ah_close_conn(void *cookie)
+ {
+ #ifdef HAVE_SSPI
+     clean_session_sspi((auth_session*)cookie);
+ #endif
+ }

 static void auth_register(ne_session *sess, int isproxy, unsigned
protomask,
               const struct auth_class *ahc, const char *id,
***************
*** 1296,1302 ****
         ne_hook_post_send(sess, ah_post_send, ahs);
         ne_hook_destroy_request(sess, ah_destroy, ahs);
         ne_hook_destroy_session(sess, free_auth, ahs);
!
         ne_set_session_private(sess, id, ahs);
     }

--- 1314,1322 ----
         ne_hook_post_send(sess, ah_post_send, ahs);
         ne_hook_destroy_request(sess, ah_destroy, ahs);
         ne_hook_destroy_session(sess, free_auth, ahs);
! #ifdef HAVE_SSPI
!         ne_hook_connection_close(sess, ah_close_conn, ahs);
! #endif
         ne_set_session_private(sess, id, ahs);
     }

diff -cr neon-0.26.3\src\ne_private.h neon-0.26.3.rjvdboon\src\ne_private.h
*** neon-0.26.3\src\ne_private.h    Tue Mar 07 22:38:22 2006
--- neon-0.26.3.rjvdboon\src\ne_private.h    Wed Feb 14 10:00:46 2007
***************
*** 87,93 ****
     int rdtimeout; /* read timeout. */

     struct hook *create_req_hooks, *pre_send_hooks, *post_send_hooks;
!     struct hook *destroy_req_hooks, *destroy_sess_hooks, *private;

     char *user_agent; /* full User-Agent: header field */

--- 87,93 ----
     int rdtimeout; /* read timeout. */

     struct hook *create_req_hooks, *pre_send_hooks, *post_send_hooks;
!     struct hook *destroy_req_hooks, *destroy_sess_hooks,
*conn_close_hooks, *private;

     char *user_agent; /* full User-Agent: header field */

diff -cr neon-0.26.3\src\ne_request.c neon-0.26.3.rjvdboon\src\ne_request.c
*** neon-0.26.3\src\ne_request.c    Tue Mar 07 22:38:22 2006
--- neon-0.26.3.rjvdboon\src\ne_request.c    Wed Feb 14 10:00:46 2007
***************
*** 1210,1216 ****
      * then it is impossible to distinguish between a server failure
      * and a connection timeout if an EOF/RST is received.  So don't
      * do that. */
!     if (!req->flags[NE_REQFLAG_IDEMPOTENT] && req->session->persisted) {
         NE_DEBUG(NE_DBG_HTTP, "req: Closing connection for non-idempotent
"
                  "request.\n");
         ne_close_connection(req->session);
--- 1210,1217 ----
      * then it is impossible to distinguish between a server failure
      * and a connection timeout if an EOF/RST is received.  So don't
      * do that. */
!     if (!req->flags[NE_REQFLAG_IDEMPOTENT] && req->session->persisted &&
!         !req->flags[NE_REQFLAG_IN_AUTH_NEGOTIATION]) {
         NE_DEBUG(NE_DBG_HTTP, "req: Closing connection for non-idempotent
"
                  "request.\n");
         ne_close_connection(req->session);
diff -cr neon-0.26.3\src\ne_request.h neon-0.26.3.rjvdboon\src\ne_request.h
*** neon-0.26.3\src\ne_request.h    Sun Feb 26 00:16:12 2006
--- neon-0.26.3.rjvdboon\src\ne_request.h    Wed Feb 14 10:00:46 2007
***************
*** 231,236 ****
--- 231,239 ----

     NE_REQFLAG_IDEMPOTENT, /* disable this flag if the request uses a
                             * non-idempotent method such as POST. */
+     NE_REQFLAG_IN_AUTH_NEGOTIATION, /* enable this flag if the request is
part of auth
+                                      * negotiation. i.e. please do not
close the connection
+                                      * or all my work was in vain... */

     NE_REQFLAG_LAST /* enum sentinel value */
 } ne_request_flag;
***************
*** 284,289 ****
--- 287,297 ----
 void ne_hook_destroy_session(ne_session *sess,
                  ne_destroy_sess_fn fn, void *userdata);

+ typedef void (*ne_connection_close_fn)(void *userdata);
+ /* Hook called when the connection is closed. */
+ void ne_hook_connection_close(ne_session *sess,
+                  ne_connection_close_fn fn, void *userdata);
+
 /* The ne_unhook_* functions remove a hook registered with the given
  * session.  If a hook is found which was registered with a given
  * function 'fn', and userdata pointer 'userdata', then it will be
***************
*** 299,304 ****
--- 307,314 ----
                                ne_destroy_req_fn fn, void *userdata);
 void ne_unhook_destroy_session(ne_session *sess,
                                ne_destroy_sess_fn fn, void *userdata);
+ void ne_unhook_connection_close(ne_session *sess,
+                                ne_connection_close_fn fn, void *userdata);

 /* Store an opaque context for the request, 'priv' is returned by a
  * call to ne_request_get_private with the same ID. */
diff -cr neon-0.26.3\src\ne_session.c neon-0.26.3.rjvdboon\src\ne_session.c
*** neon-0.26.3\src\ne_session.c    Wed Mar 01 18:45:02 2006
--- neon-0.26.3.rjvdboon\src\ne_session.c    Thu Feb 15 12:57:20 2007
***************
*** 71,76 ****
--- 71,78 ----
     destroy_hooks(sess->post_send_hooks);
     destroy_hooks(sess->destroy_req_hooks);
     destroy_hooks(sess->destroy_sess_hooks);
+     destroy_hooks(sess->conn_close_hooks);
+     sess->conn_close_hooks = NULL; // Set to NULL explicitly, else would
be called by the close_connection below.
     destroy_hooks(sess->private);

     ne_free(sess->scheme);
***************
*** 256,263 ****

 void ne_close_connection(ne_session *sess)
 {
!     if (sess->connected) {
     NE_DEBUG(NE_DBG_SOCKET, "Closing connection.\n");
     ne_sock_close(sess->socket);
     sess->socket = NULL;
     NE_DEBUG(NE_DBG_SOCKET, "Connection closed.\n");
--- 258,272 ----

 void ne_close_connection(ne_session *sess)
 {
!     struct hook *hk, *next_hk;
     NE_DEBUG(NE_DBG_SOCKET, "Closing connection.\n");
+     if (sess->connected) {
+         NE_DEBUG(NE_DBG_HTTP, "Running connection close hooks.\n");
+         for (hk = sess->conn_close_hooks; hk; hk = next_hk) {
+             ne_connection_close_fn fn = (ne_connection_close_fn)hk->fn;
+             next_hk = hk->next;
+             fn(hk->userdata);
+         }
     ne_sock_close(sess->socket);
     sess->socket = NULL;
     NE_DEBUG(NE_DBG_SOCKET, "Connection closed.\n");
***************
*** 396,401 ****
--- 405,416 ----
     ADD_HOOK(sess->destroy_sess_hooks, fn, userdata);
 }

+ void ne_hook_connection_close(ne_session *sess,
+                  ne_connection_close_fn fn, void *userdata)
+ {
+     ADD_HOOK(sess->conn_close_hooks, fn, userdata);
+ }
+
 void ne_set_session_private(ne_session *sess, const char *id, void
*userdata)
 {
     add_hook(&sess->private, id, NULL, userdata);
***************
*** 444,447 ****
--- 459,468 ----
                                ne_destroy_sess_fn fn, void *userdata)
 {
     REMOVE_HOOK(sess->destroy_sess_hooks, fn, userdata);
+ }
+
+ void ne_unhook_connection_close(ne_session *sess,
+                                ne_connection_close_fn fn, void *userdata)
+ {
+     REMOVE_HOOK(sess->conn_close_hooks, fn, userdata);
 }
diff -cr neon-0.26.3\src\ne_sspi.c neon-0.26.3.rjvdboon\src\ne_sspi.c
*** neon-0.26.3\src\ne_sspi.c    Tue Sep 05 16:43:48 2006
--- neon-0.26.3.rjvdboon\src\ne_sspi.c    Wed Feb 14 10:00:46 2007
***************
*** 29,39 ****

 #define SEC_SUCCESS(Status) ((Status) >= 0)

 struct SSPIContextStruct {
     CtxtHandle context;
     char *serverName;
     CredHandle credentials;
!     int continueNeeded;
     int authfinished;
     char *mechanism;
     int ntlm;
--- 29,46 ----

 #define SEC_SUCCESS(Status) ((Status) >= 0)

+ /* Defined session flags: */
+ typedef enum {
+     ne_sspistatus_unknown = 0, /* unknown state, sspi unused */
+     ne_sspistatus_continueneeded, /* authentication in progress */
+     ne_sspistatus_authfinishedthisrequest /* authentication succeeded in
last request */
+ } ne_sspi_status;
+
 struct SSPIContextStruct {
     CtxtHandle context;
     char *serverName;
     CredHandle credentials;
!     ne_sspi_status authState;
     int authfinished;
     char *mechanism;
     int ntlm;
***************
*** 338,344 ****
     }

     sspiContext = ne_calloc(sizeof(SSPIContext));
-     sspiContext->continueNeeded = 0;

     if (ntlm) {
         sspiContext->mechanism = "NTLM";
--- 345,350 ----
***************
*** 352,357 ****
--- 358,364 ----

     sspiContext->ntlm = ntlm;
     sspiContext->authfinished = 0;
+     sspiContext->authState = ne_sspistatus_unknown;
     *context = sspiContext;
     return 0;
 }
***************
*** 367,373 ****
 #else
     pSFT->FreeCredentialsHandle(&(sspiContext->credentials));
 #endif
!     sspiContext->continueNeeded = 0;
 }

 /*
--- 374,380 ----
 #else
     pSFT->FreeCredentialsHandle(&(sspiContext->credentials));
 #endif
!     sspiContext->authState = ne_sspistatus_unknown;
 }

 /*
***************
*** 428,433 ****
--- 435,441 ----
         return status;
     }
     sspiContext->authfinished = 0;
+     sspiContext->authState = ne_sspistatus_unknown;
     return 0;
 }
 /*
***************
*** 441,446 ****
--- 449,455 ----
     int status;
     SECURITY_STATUS securityStatus;
     ULONG contextFlags;
+     int continueNeeded = 0;

     SSPIContext *sspiContext;
     if (initialized <= 0) {
***************
*** 463,473 ****
         return status;
     }

     if (base64Token) {
         SecBufferDesc inBufferDesc;
         SecBuffer inBuffer;

!         if (!sspiContext->continueNeeded) {
             freeBuffer(&outBufferDesc);
             NE_DEBUG(NE_DBG_HTTPAUTH, "sspi: Got an unexpected token.\n");
             return -1;
--- 472,485 ----
         return status;
     }

+     if (sspiContext->authState == ne_sspistatus_continueneeded)
+         continueNeeded = 1;
+     sspiContext->authState = ne_sspistatus_unknown;
     if (base64Token) {
         SecBufferDesc inBufferDesc;
         SecBuffer inBuffer;

!         if (!continueNeeded) {
             freeBuffer(&outBufferDesc);
             NE_DEBUG(NE_DBG_HTTPAUTH, "sspi: Got an unexpected token.\n");
             return -1;
***************
*** 490,499 ****
         if (securityStatus == SEC_E_OK)
         {
             sspiContext->authfinished = 1;
         }
         freeBuffer(&inBufferDesc);
     } else {
!         if (sspiContext->continueNeeded) {
             freeBuffer(&outBufferDesc);
             NE_DEBUG(NE_DBG_HTTPAUTH, "sspi: Expected a token from
server.\n");
             return -1;
--- 502,512 ----
         if (securityStatus == SEC_E_OK)
         {
             sspiContext->authfinished = 1;
+             sspiContext->authState =
ne_sspistatus_authfinishedthisrequest;
         }
         freeBuffer(&inBufferDesc);
     } else {
!         if (continueNeeded) {
             freeBuffer(&outBufferDesc);
             NE_DEBUG(NE_DBG_HTTPAUTH, "sspi: Expected a token from
server.\n");
             return -1;
***************
*** 541,549 ****

     if (securityStatus == SEC_I_COMPLETE_AND_CONTINUE
         || securityStatus == SEC_I_CONTINUE_NEEDED) {
!         sspiContext->continueNeeded = 1;
     } else {
!         sspiContext->continueNeeded = 0;
     }

     if (!(securityStatus == SEC_I_COMPLETE_AND_CONTINUE
--- 554,563 ----

     if (securityStatus == SEC_I_COMPLETE_AND_CONTINUE
         || securityStatus == SEC_I_CONTINUE_NEEDED) {
!         sspiContext->authState = ne_sspistatus_continueneeded;
     } else {
!         if (securityStatus == SEC_I_COMPLETE_NEEDED || securityStatus ==
SEC_E_OK)
!             sspiContext->authState =
ne_sspistatus_authfinishedthisrequest;
     }

     if (!(securityStatus == SEC_I_COMPLETE_AND_CONTINUE
***************
*** 562,566 ****
--- 576,601 ----
     freeBuffer(&outBufferDesc);

     return 0;
+ }
+
+ int ne_sspi_is_in_negotiation(void *context)
+ {
+     int status;
+     SSPIContext *sspiContext;
+     if (initialized <= 0) {
+         return 0;
+     }
+
+     status = getContext(context, &sspiContext);
+     if (status) {
+         return 0;
+     }
+
+     NE_DEBUG(NE_DBG_HTTPAUTH, "sspi: ne_sspi_is_in_negotiation. (%d)\n",
sspiContext->authState);
+
+     if (sspiContext->authState == ne_sspistatus_continueneeded ||
+         sspiContext->authState == ne_sspistatus_authfinishedthisrequest)
+         return 1;
+     return 0;
 }
 #endif /* HAVE_SSPI */
diff -cr neon-0.26.3\src\ne_sspi.h neon-0.26.3.rjvdboon\src\ne_sspi.h
*** neon-0.26.3\src\ne_sspi.h    Sun Feb 12 13:05:14 2006
--- neon-0.26.3.rjvdboon\src\ne_sspi.h    Wed Feb 14 10:00:46 2007
***************
*** 43,48 ****
--- 43,50 ----
 int ne_sspi_authenticate(void *context, const char *base64Token,
                          char **responseToken);

+ int ne_sspi_is_in_negotiation(void *context);
+
 #endif /* HAVE_SSPI */

 #endif /* NE_SSPI_H */
_______________________________________________
neon mailing list
[email protected]
http://mailman.webdav.org/mailman/listinfo/neon

Reply via email to