diff --git a/gnome-vfs2.spec b/gnome-vfs2.spec index 9204883..5387fd4 100644 --- a/gnome-vfs2.spec +++ b/gnome-vfs2.spec @@ -9,7 +9,7 @@ Summary: The GNOME virtual file-system libraries. Name: gnome-vfs2 Version: 2.10.0 -Release: 3 +Release: 4 License: LGPL Group: System Environment/Libraries Source0: gnome-vfs-%{version}.tar.bz2 @@ -57,6 +57,9 @@ Patch202: gnome-vfs-2.9.91-hal-api-2.patch # possible gcc4 bug Patch300: gnome-vfs-2.9.91-fix-gcc4-build.patch +# Bug 150132 +Patch400: neon-0.24.7-gssapi.patch + %description GNOME VFS is the GNOME virtual file system. It is the foundation of the Nautilus file manager. It provides a modular architecture and @@ -110,6 +113,8 @@ cp -f %{SOURCE1} modules %patch300 -p0 -b .gcc4-build +%patch400 -d imported/neon -p2 -b .neon-gssapi + %build # needed for patch6 (changing makefile to old vfolder backend) @@ -190,6 +195,9 @@ done %config %{_sysconfdir}/gnome-vfs-2.0/modules/smb-module.conf %changelog +* Fri Apr 29 2005 David Zeuthen 2.10.0-4 +- Make local neon copy support gssapi correctly (#150132) + * Tue Mar 29 2005 David Zeuthen 2.10.0-3 - Rebuild diff --git a/neon-0.24.7-gssapi.patch b/neon-0.24.7-gssapi.patch new file mode 100644 index 0000000..1f56da9 --- /dev/null +++ b/neon-0.24.7-gssapi.patch @@ -0,0 +1,516 @@ + +Update to the GSSAPI implementation from HEAD. + +--- neon/src/ne_auth.c (revision 309) ++++ neon/src/ne_auth.c (working copy) +@@ -161,8 +161,11 @@ + /* This used for Basic auth */ + char *basic; + #ifdef HAVE_GSSAPI +- /* This used for GSSAPI auth */ ++ /* for the GSSAPI/Negotiate scheme: */ + char *gssapi_token; ++ gss_ctx_id_t gssctx; ++ gss_name_t gssname; ++ gss_OID gssmech; + #endif + /* These all used for Digest auth */ + char *realm; +@@ -202,7 +205,7 @@ + struct ne_md5_ctx response_body; + + /* Results of response-header callbacks */ +- char *auth_hdr, *auth_info_hdr; ++ ne_buffer *auth_hdr, *auth_info_hdr; + }; + + static void clean_session(auth_session *sess) +@@ -214,6 +217,17 @@ + NE_FREE(sess->opaque); + NE_FREE(sess->realm); + #ifdef HAVE_GSSAPI ++ { ++ int major; ++ ++ if (sess->gssctx != GSS_C_NO_CONTEXT) ++ gss_delete_sec_context(&major, sess->gssctx, GSS_C_NO_BUFFER); ++ ++ if (sess->gssmech != GSS_C_NO_OID) { ++ gss_release_oid(&major, &sess->gssmech); ++ sess->gssmech = GSS_C_NO_OID; ++ } ++ } + NE_FREE(sess->gssapi_token); + #endif + } +@@ -321,69 +335,162 @@ + /* Add GSSAPI authentication credentials to a request */ + static char *request_gssapi(auth_session *sess) + { +- return ne_concat("Negotiate ", sess->gssapi_token, "\r\n", NULL); ++ if (sess->gssapi_token) ++ return ne_concat("Negotiate ", sess->gssapi_token, "\r\n", NULL); ++ else ++ return NULL; + } + +-static int get_gss_name(gss_name_t *server, auth_session *sess) ++/* Create an GSSAPI name for server HOSTNAME; returns non-zero on ++ * error. */ ++static void get_gss_name(gss_name_t *server, const char *hostname) + { +- unsigned int major_status, minor_status; ++ unsigned int major, minor; + gss_buffer_desc token = GSS_C_EMPTY_BUFFER; + +- token.value = ne_concat("HTTP@", sess->sess->server.hostname, NULL); ++ token.value = ne_concat("HTTP@", hostname, NULL); + token.length = strlen(token.value); + +- major_status = gss_import_name(&minor_status, &token, +- GSS_C_NT_HOSTBASED_SERVICE, +- server); +- return GSS_ERROR(major_status) ? -1 : 0; ++ major = gss_import_name(&minor, &token, GSS_C_NT_HOSTBASED_SERVICE, ++ server); ++ ne_free(token.value); ++ ++ if (GSS_ERROR(major)) { ++ NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: gss_import_name failed.\n"); ++ *server = GSS_C_NO_NAME; ++ } + } + +-/* Examine a GSSAPI auth challenge; returns 0 if a valid challenge, +- * else non-zero. */ +-static int +-gssapi_challenge(auth_session *sess, struct auth_challenge *parms) ++/* Append GSSAPI error(s) for STATUS of type TYPE to BUF; prepending ++ * ": " to each error if *FLAG is non-zero, setting *FLAG after an ++ * error has been appended. */ ++static void make_gss_error(ne_buffer *buf, int *flag, ++ unsigned int status, int type) + { +- gss_ctx_id_t context; +- gss_name_t server_name; +- unsigned int major_status, minor_status; +- gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; +- gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; ++ int major, minor; ++ int context = 0; ++ ++ do { ++ gss_buffer_desc msg; ++ major = gss_display_status(&minor, status, type, ++ GSS_C_NO_OID, &context, &msg); ++ if (major == GSS_S_COMPLETE && msg.length) { ++ if ((*flag)++) ne_buffer_append(buf, ": ", 2); ++ ne_buffer_append(buf, msg.value, msg.length); ++ } ++ if (msg.length) gss_release_buffer(&minor, &msg); ++ } while (context); ++} + +- clean_session(sess); ++/* Continue a GSS-API Negotiate exchange, using input TOKEN if ++ * non-NULL. Returns non-zero on error. */ ++static int continue_negotiate(auth_session *sess, const char *token) ++{ ++ unsigned int major, minor; ++ gss_buffer_desc input = GSS_C_EMPTY_BUFFER; ++ gss_buffer_desc output = GSS_C_EMPTY_BUFFER; ++ unsigned char *bintoken = NULL; ++ int ret; ++ gss_OID mech = sess->gssmech; + +- if (get_gss_name(&server_name, sess)) ++ if (token) { ++ input.length = ne_unbase64(token, &bintoken); ++ if (input.length == 0) { ++ NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: Invalid input [%s].\n", ++ token); ++ return -1; ++ } ++ input.value = bintoken; ++ NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: Continuation token [%s]\n", token); ++ } ++ else if (sess->gssctx != GSS_C_NO_CONTEXT) { ++ NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: Reset incomplete context.\n"); ++ gss_delete_sec_context(&minor, &sess->gssctx, GSS_C_NO_BUFFER); ++ } ++ ++ major = gss_init_sec_context(&minor, GSS_C_NO_CREDENTIAL, &sess->gssctx, ++ sess->gssname, mech, ++ GSS_C_MUTUAL_FLAG, GSS_C_INDEFINITE, ++ GSS_C_NO_CHANNEL_BINDINGS, ++ &input, &sess->gssmech, &output, NULL, NULL); ++ ++ /* done with the input token. */ ++ if (bintoken) ne_free(bintoken); ++ ++ if (GSS_ERROR(major)) { ++ ne_buffer *err = ne_buffer_create(); ++ int flag = 0; ++ ++ make_gss_error(err, &flag, major, GSS_C_GSS_CODE); ++ make_gss_error(err, &flag, minor, GSS_C_MECH_CODE); ++ NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: Error: %s\n", err->data); ++ ne_set_error(sess->sess, _("GSSAPI authentication error (%s)"), ++ err->data); ++ ne_buffer_destroy(err); + return -1; ++ } + +- major_status = gss_init_sec_context(&minor_status, +- GSS_C_NO_CREDENTIAL, +- &context, +- server_name, +- GSS_C_NO_OID, +- 0, +- GSS_C_INDEFINITE, +- GSS_C_NO_CHANNEL_BINDINGS, +- &input_token, +- NULL, +- &output_token, +- NULL, +- NULL); +- gss_release_name(&minor_status, &server_name); ++ if (major == GSS_S_CONTINUE_NEEDED || major == GSS_S_COMPLETE) { ++ NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: init_sec_context OK. (major=%d)\n", ++ major); ++ ret = 0; ++ } ++ else { ++ NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: Init failure %d.\n", major); ++ ret = -1; ++ } + +- if (GSS_ERROR(major_status)) { +- NE_DEBUG(NE_DBG_HTTPAUTH, "gss_init_sec_context failed.\n"); ++ if (major != GSS_S_CONTINUE_NEEDED) { ++ /* context no longer needed: destroy it */ ++ gss_delete_sec_context(&minor, &sess->gssctx, GSS_C_NO_BUFFER); ++ } ++ ++ if (output.length) { ++ sess->gssapi_token = ne_base64(output.value, output.length); ++ NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: Output token: [%s]\n", ++ sess->gssapi_token); ++ gss_release_buffer(&minor, &output); ++ } else { ++ NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: No output token.\n"); ++ } ++ ++ return ret; ++} ++ ++/* Process a Negotiate challange CHALL in session SESS; returns zero ++ * if challenge is accepted. */ ++static int gssapi_challenge(auth_session *sess, struct auth_challenge *chall) ++{ ++ int ret = continue_negotiate(sess, chall->opaque); ++ if (ret == 0) ++ sess->scheme = auth_scheme_gssapi; ++ return ret; ++} ++ ++/* Verify the header HDR in a Negotiate response. */ ++static int verify_negotiate_response(auth_session *sess, char *hdr) ++{ ++ char *sep, *ptr = strchr(hdr, ' '); ++ ++ if (strncmp(hdr, "Negotiate", ptr - hdr) != 0) { ++ NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: Not a Negotiate response!\n"); + return -1; + } + +- if (output_token.length == 0) +- return -1; ++ ptr++; + +- sess->gssapi_token = ne_base64(output_token.value, output_token.length); +- gss_release_buffer(&major_status, &output_token); ++ if (strlen(ptr) == 0) { ++ NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: No token in Negotiate response!\n"); ++ return 0; ++ } + +- NE_DEBUG(NE_DBG_HTTPAUTH, +- "Base64 encoded GSSAPI challenge: %s.\n", sess->gssapi_token); +- sess->scheme = auth_scheme_gssapi; +- return 0; ++ if ((sep = strchr(ptr, ',')) != NULL) ++ *sep = '\0'; ++ if ((sep = strchr(ptr, ' ')) != NULL) ++ *sep = '\0'; ++ ++ NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: Negotiate response token [%s]\n", ptr); ++ return continue_negotiate(sess, ptr); + } + #endif + +@@ -637,7 +744,8 @@ + *pnt = '\0'; + *value = pnt + 1; + state = AFTER_EQ; +- } else if (*pnt == ' ' && ischall && *key != NULL) { ++ } else if ((*pnt == ' ' || *pnt == ',') ++ && ischall && *key != NULL) { + *value = NULL; + *pnt = '\0'; + *hdr = pnt + 1; +@@ -679,8 +787,8 @@ + * 0 if it gives a valid authentication for the server + * non-zero otherwise (don't believe the response in this case!). + */ +-static int verify_response(struct auth_request *req, auth_session *sess, +- const char *value) ++static int verify_digest_response(struct auth_request *req, auth_session *sess, ++ const char *value) + { + char *hdr, *pnt, *key, *val; + auth_qop qop = auth_qop_none; +@@ -842,36 +950,39 @@ + while (!tokenize(&pnt, &key, &val, 1)) { + + if (val == NULL) { +- /* We have a new challenge */ +- NE_DEBUG(NE_DBG_HTTPAUTH, "New challenge for scheme [%s]\n", key); +- chall = ne_calloc(sizeof *chall); ++ auth_scheme scheme; + +- chall->next = challenges; +- challenges = chall; +- /* Initialize the challenge parameters */ +- /* Which auth-scheme is it (case-insensitive matching) */ + if (strcasecmp(key, "basic") == 0) { +- NE_DEBUG(NE_DBG_HTTPAUTH, "Basic scheme.\n"); +- chall->scheme = auth_scheme_basic; ++ scheme = auth_scheme_basic; + } else if (strcasecmp(key, "digest") == 0) { +- NE_DEBUG(NE_DBG_HTTPAUTH, "Digest scheme.\n"); +- chall->scheme = auth_scheme_digest; +-#ifdef HAVE_GSSAPI ++ scheme = auth_scheme_digest; ++ } ++#ifdef HAVE_GSSAPI ++ /* cope with a Negotiate parameter which doesn't match the ++ * auth-param due to the broken spec. */ ++ else if (chall && chall->scheme == auth_scheme_gssapi ++ && chall->opaque == NULL) { ++ chall->opaque = key; ++ continue; + } else if (strcasecmp(key, "negotiate") == 0) { +- NE_DEBUG(NE_DBG_HTTPAUTH, "GSSAPI scheme.\n"); +- chall->scheme = auth_scheme_gssapi; ++ scheme = auth_scheme_gssapi; ++ } + #endif +- } else { +- NE_DEBUG(NE_DBG_HTTPAUTH, "Unknown scheme.\n"); +- ne_free(chall); +- challenges = NULL; +- break; ++ else { ++ NE_DEBUG(NE_DBG_HTTPAUTH, "Ignoring challenge '%s'.\n", key); ++ chall = NULL; ++ continue; + } ++ ++ NE_DEBUG(NE_DBG_HTTPAUTH, "New '%s' challenge.\n", key); ++ chall = ne_calloc(sizeof *chall); ++ chall->scheme = scheme; ++ chall->next = challenges; ++ challenges = chall; + continue; + } else if (chall == NULL) { +- /* If we haven't got an auth-scheme, and we're +- * haven't yet found a challenge, skip this pair. +- */ ++ /* Ignore pairs for an unknown challenge. */ ++ NE_DEBUG(NE_DBG_HTTPAUTH, "Ignored pair: %s = %s\n", key, val); + continue; + } + +@@ -924,15 +1035,17 @@ + success = 0; + + #ifdef HAVE_GSSAPI +- if (strcmp(ne_get_scheme(sess->sess), "https") == 0) { ++ /* Ignore Negotiate challenges from origin servers which don't ++ * come over SSL. */ ++ if (sess->spec == &ah_proxy_class || sess->context != AUTH_ANY) { + NE_DEBUG(NE_DBG_HTTPAUTH, "Looking for GSSAPI.\n"); + /* Try a GSSAPI challenge */ + for (chall = challenges; chall != NULL; chall = chall->next) { + if (chall->scheme == auth_scheme_gssapi) { +- if (!gssapi_challenge(sess, chall)) { +- success = 1; +- break; +- } ++ if (!gssapi_challenge(sess, chall)) { ++ success = 1; ++ break; ++ } + } + } + } +@@ -993,6 +1106,14 @@ + ne_md5_process_bytes(block, length, ctx); + } + ++/* Collect auth challenges into an ne_buffer */ ++static void ah_collect_header(void *userdata, const char *value) ++{ ++ ne_buffer *ar = userdata; ++ if (ne_buffer_size(ar)) ne_buffer_append(ar, ", ", 2); ++ ne_buffer_zappend(ar, value); ++} ++ + static void ah_create(ne_request *req, void *session, const char *method, + const char *uri) + { +@@ -1009,14 +1130,14 @@ + areq->method = method; + areq->uri = uri; + areq->request = req; ++ areq->auth_hdr = ne_buffer_create(); ++ areq->auth_info_hdr = ne_buffer_create(); + + ne_add_response_header_handler(req, sess->spec->resp_hdr, +- ne_duplicate_header, &areq->auth_hdr); +- ++ ah_collect_header, areq->auth_hdr); + + ne_add_response_header_handler(req, sess->spec->resp_info_hdr, +- ne_duplicate_header, +- &areq->auth_info_hdr); ++ ah_collect_header, areq->auth_info_hdr); + + sess->attempt = 0; + +@@ -1035,7 +1156,7 @@ + } else { + char *value; + +- NE_DEBUG(NE_DBG_HTTPAUTH, "Handling."); ++ NE_DEBUG(NE_DBG_HTTPAUTH, "Handling auth session.\n"); + req->will_handle = 1; + + if (sess->qop == auth_qop_auth_int) { +@@ -1082,22 +1203,44 @@ + + if (!areq) return NE_OK; + ++#ifdef HAVE_GSSAPI ++ /* whatever happens: forget the GSSAPI token cached thus far */ ++ if (sess->gssapi_token) { ++ ne_free(sess->gssapi_token); ++ sess->gssapi_token = NULL; ++ } ++#endif ++ + NE_DEBUG(NE_DBG_HTTPAUTH, + "ah_post_send (#%d), code is %d (want %d), %s is %s\n", + sess->attempt, status->code, sess->spec->status_code, +- sess->spec->resp_hdr, SAFELY(areq->auth_hdr)); +- if (areq->auth_info_hdr != NULL && +- verify_response(areq, sess, areq->auth_info_hdr)) { +- NE_DEBUG(NE_DBG_HTTPAUTH, "Response authentication invalid.\n"); +- ne_set_error(sess->sess, "%s", _(sess->spec->fail_msg)); +- ret = NE_ERROR; +- } else if ((status->code == sess->spec->status_code || +- (status->code == 401 && sess->context == AUTH_CONNECT)) && +- areq->auth_hdr != NULL) { ++ sess->spec->resp_hdr, areq->auth_hdr->data); ++ if (ne_buffer_size(areq->auth_info_hdr) ++ && sess->scheme == auth_scheme_digest) { ++ if (verify_digest_response(areq, sess, areq->auth_info_hdr->data)) { ++ NE_DEBUG(NE_DBG_HTTPAUTH, "Response authentication invalid.\n"); ++ ne_set_error(sess->sess, "%s", _(sess->spec->fail_msg)); ++ ret = NE_ERROR; ++ } ++ } ++#ifdef HAVE_GSSAPI ++ /* one must wonder... has Mr Brezak actually read RFC2617? */ ++ else if (sess->scheme == auth_scheme_gssapi ++ && (status->klass == 2 || status->klass == 3) ++ && ne_buffer_size(areq->auth_hdr)) { ++ if (verify_negotiate_response(sess, areq->auth_hdr->data)) { ++ NE_DEBUG(NE_DBG_HTTPAUTH, "gssapi: Mutual auth failed.\n"); ++ ret = NE_ERROR; ++ } ++ } ++#endif /* HAVE_GSSAPI */ ++ else if ((status->code == sess->spec->status_code || ++ (status->code == 401 && sess->context == AUTH_CONNECT)) && ++ ne_buffer_size(areq->auth_hdr)) { + /* note above: allow a 401 in response to a CONNECT request + * from a proxy since some buggy proxies send that. */ +- NE_DEBUG(NE_DBG_HTTPAUTH, "Got challenge with code %d.\n", status->code); +- if (!auth_challenge(sess, areq->auth_hdr)) { ++ NE_DEBUG(NE_DBG_HTTPAUTH, "Got challenge (code %d).\n", status->code); ++ if (!auth_challenge(sess, areq->auth_hdr->data)) { + ret = NE_RETRY; + } else { + clean_session(sess); +@@ -1105,9 +1248,9 @@ + } + } + +- NE_FREE(areq->auth_info_hdr); +- NE_FREE(areq->auth_hdr); +- ++ ne_buffer_clear(areq->auth_hdr); ++ ne_buffer_clear(areq->auth_info_hdr); ++ + return ret; + } + +@@ -1115,13 +1258,25 @@ + { + auth_session *sess = session; + struct auth_request *areq = ne_get_request_private(req, sess->spec->id); +- if (areq) ne_free(areq); ++ ++ if (areq) { ++ ne_buffer_destroy(areq->auth_info_hdr); ++ ne_buffer_destroy(areq->auth_hdr); ++ ne_free(areq); ++ } + } + + static void free_auth(void *cookie) + { + auth_session *sess = cookie; + ++#ifdef HAVE_GSSAPI ++ if (sess->gssname != GSS_C_NO_NAME) { ++ int major; ++ gss_release_name(&major, sess->gssname); ++ } ++#endif ++ + clean_session(sess); + ne_free(sess); + } +@@ -1137,10 +1292,17 @@ + ahs->sess = sess; + ahs->spec = ahc; + +- if (strcmp(ne_get_scheme(sess), "https") == 0) ++ if (strcmp(ne_get_scheme(sess), "https") == 0) { + ahs->context = isproxy ? AUTH_CONNECT : AUTH_NOTCONNECT; +- else ++ } else { + ahs->context = AUTH_ANY; ++ } ++#ifdef HAVE_GSSAPI ++ get_gss_name(&ahs->gssname, (isproxy ? sess->proxy.hostname ++ : sess->server.hostname)); ++ ahs->gssctx = GSS_C_NO_CONTEXT; ++ ahs->gssmech = GSS_C_NO_OID; ++#endif + + /* Register hooks */ + ne_hook_create_request(sess, ah_create, ahs);