- Make local neon copy support gssapi correctly (#150132)

f38
David Zeuthen 20 years ago
parent b9773b1340
commit ea11941c4d

@ -9,7 +9,7 @@
Summary: The GNOME virtual file-system libraries. Summary: The GNOME virtual file-system libraries.
Name: gnome-vfs2 Name: gnome-vfs2
Version: 2.10.0 Version: 2.10.0
Release: 3 Release: 4
License: LGPL License: LGPL
Group: System Environment/Libraries Group: System Environment/Libraries
Source0: gnome-vfs-%{version}.tar.bz2 Source0: gnome-vfs-%{version}.tar.bz2
@ -57,6 +57,9 @@ Patch202: gnome-vfs-2.9.91-hal-api-2.patch
# possible gcc4 bug # possible gcc4 bug
Patch300: gnome-vfs-2.9.91-fix-gcc4-build.patch Patch300: gnome-vfs-2.9.91-fix-gcc4-build.patch
# Bug 150132
Patch400: neon-0.24.7-gssapi.patch
%description %description
GNOME VFS is the GNOME virtual file system. It is the foundation of GNOME VFS is the GNOME virtual file system. It is the foundation of
the Nautilus file manager. It provides a modular architecture and the Nautilus file manager. It provides a modular architecture and
@ -110,6 +113,8 @@ cp -f %{SOURCE1} modules
%patch300 -p0 -b .gcc4-build %patch300 -p0 -b .gcc4-build
%patch400 -d imported/neon -p2 -b .neon-gssapi
%build %build
# needed for patch6 (changing makefile to old vfolder backend) # needed for patch6 (changing makefile to old vfolder backend)
@ -190,6 +195,9 @@ done
%config %{_sysconfdir}/gnome-vfs-2.0/modules/smb-module.conf %config %{_sysconfdir}/gnome-vfs-2.0/modules/smb-module.conf
%changelog %changelog
* Fri Apr 29 2005 David Zeuthen <davidz@redhat.com> 2.10.0-4
- Make local neon copy support gssapi correctly (#150132)
* Tue Mar 29 2005 David Zeuthen <davidz@redhat.com> 2.10.0-3 * Tue Mar 29 2005 David Zeuthen <davidz@redhat.com> 2.10.0-3
- Rebuild - Rebuild

@ -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);
Loading…
Cancel
Save