You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
gnome-vfs2/neon-0.24.7-gssapi.patch

517 lines
17 KiB

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);