|
View:
New views
7 Messages
—
Rating Filter:
Alert me
|
|
|
Moving ra_serf authn handlers to serf.As already mentioned at SubConf last week, I'm moving the code that
handles the authentication schemes in ra_serf from Subversion to Serf. The main goal for me is to be able to add features to serf that rely on authentication being in serf too (e.g. SSL tunneling over HTTP proxies). It also just seems more logical to do the authentication in the http library instead of the application, and this might help other users of serf. The code I want to move is parts of libsvn_ra_serf/util.c and all of the files libsvn_ra_serf/(auth.*|auth_digest.*|auth_kerb.*|win32_auth_sspi.*). Most of these have been written by Justin and me, but there were some fixes, cleanups, buildscripts by other svn committers. Both Subversion and Serf are covered by the Apache License 2.0, so I don't think there's a problem in this area. However, people have suggested to add a line in serf/NOTICE that says Serf includes code developed by the Subversion Corporation. Attached my WIP patches for serf and subversion, including the changed NOTICE file. Thoughts? Remarks? Lieven ------------------------------------------------------ http://subversion.tigris.org/ds/viewMessage.do?dsForumId=462&dsMessageId=2414228 Index: subversion/libsvn_ra_serf/serf.c =================================================================== --- subversion/libsvn_ra_serf/serf.c (revision 40357) +++ subversion/libsvn_ra_serf/serf.c (working copy) @@ -303,9 +303,11 @@ load_config(svn_ra_serf__session_t *session, else session->using_proxy = FALSE; - /* Load the list of support authn types. */ - SVN_ERR(load_http_auth_types(pool, config, server_group, - &session->authn_types)); + /* Setup authentication. */ + SVN_ERR(load_http_auth_types(pool, config, server_group, + &session->authn_types)); + /* serf_config_authn_types(session->context, session->authn_types);*/ + serf_config_credentials_callback(session->context, svn_ra_serf__credentials_callback); return SVN_NO_ERROR; } @@ -430,11 +432,15 @@ svn_ra_serf__open(svn_ra_session_t *session, serf_sess->conns[0]->useragent = USER_AGENT; /* go ahead and tell serf about the connection. */ - serf_sess->conns[0]->conn = - serf_connection_create(serf_sess->context, serf_sess->conns[0]->address, - svn_ra_serf__conn_setup, serf_sess->conns[0], - svn_ra_serf__conn_closed, serf_sess->conns[0], - serf_sess->pool); + status = + serf_connection_create2(&serf_sess->conns[0]->conn, + serf_sess->context, + url, + svn_ra_serf__conn_setup, serf_sess->conns[0], + svn_ra_serf__conn_closed, serf_sess->conns[0], + serf_sess->pool); + if (status) + return status; /* Set the progress callback. */ serf_context_set_progress_cb(serf_sess->context, svn_ra_serf__progress, Index: subversion/libsvn_ra_serf/util.c =================================================================== --- subversion/libsvn_ra_serf/util.c (revision 40357) +++ subversion/libsvn_ra_serf/util.c (working copy) @@ -504,8 +504,9 @@ svn_ra_serf__setup_serf_req(serf_request_t *reques { serf_bucket_t *hdrs_bkt; - *req_bkt = serf_bucket_request_create(method, url, body_bkt, - serf_request_get_alloc(request)); + *req_bkt = + serf_request_bucket_request_create(request, url, body_bkt, + serf_request_get_alloc(request)); hdrs_bkt = serf_bucket_request_get_headers(*req_bkt); serf_bucket_headers_setn(hdrs_bkt, "Host", conn->hostinfo); @@ -1177,6 +1178,75 @@ svn_ra_serf__handle_server_error(serf_request_t *r return server_err.error; } +apr_status_t +svn_ra_serf__credentials_callback(char **username, char **password, + serf_request_t *request, void *baton, + int code, const char *authn_type, + const char *realm, + apr_pool_t *pool) +{ + svn_ra_serf__handler_t *ctx = baton; + svn_ra_serf__session_t *session = ctx->session; + void *creds; + svn_auth_cred_simple_t *simple_creds; + svn_error_t *err; + + if (code == 401) + { + /* Use svn_auth_first_credentials if this is the first time we ask for + credentials during this session OR if the last time we asked + session->auth_state wasn't set (eg. if the credentials provider was + cancelled by the user). */ + if (!session->auth_state) + { + err = svn_auth_first_credentials(&creds, + &session->auth_state, + SVN_AUTH_CRED_SIMPLE, + realm, + session->wc_callbacks->auth_baton, + session->pool); + } + else + { + err = svn_auth_next_credentials(&creds, + session->auth_state, + session->pool); + } + + if (err) + { + ctx->session->pending_error = err; + return err->apr_err; + } + + session->auth_attempts++; + + if (!creds || session->auth_attempts > 4) + { + /* No more credentials. */ + ctx->session->pending_error = + svn_error_create(SVN_ERR_AUTHN_FAILED, NULL, + "No more credentials or we tried too many times.\n" + "Authentication failed");; + return SVN_ERR_AUTHN_FAILED; + } + + simple_creds = creds; + *username = apr_pstrdup(pool, simple_creds->username); + *password = apr_pstrdup(pool, simple_creds->password); + } + else + { + *username = apr_pstrdup(pool, session->proxy_username); + *password = apr_pstrdup(pool, session->proxy_password); + } + + printf("user: %s, pass: %s\n", *username, *password); + ctx->conn->last_status_code = code; + + return APR_SUCCESS; +} + /* Implements the serf_response_handler_t interface. Wait for HTTP response status and headers, and invoke CTX->response_handler() to carry out operation-specific processing. Afterwards, check for Index: subversion/libsvn_ra_serf/update.c =================================================================== --- subversion/libsvn_ra_serf/update.c (revision 40357) +++ subversion/libsvn_ra_serf/update.c (working copy) @@ -2127,7 +2127,7 @@ link_path(void *report_baton, * if the number of ACTIVE_REQS > REQS_PER_CONN or if there currently is * only one main connection open. */ -static void +static svn_error_t * open_connection_if_needed(svn_ra_serf__session_t *sess, int active_reqs) { /* For each REQS_PER_CONN outstanding requests open a new connection, with @@ -2136,6 +2136,7 @@ open_connection_if_needed(svn_ra_serf__session_t * ((active_reqs / REQS_PER_CONN) > sess->num_conns)) { int cur = sess->num_conns; + apr_status_t status; sess->conns[cur] = apr_palloc(sess->pool, sizeof(*sess->conns[cur])); sess->conns[cur]->bkt_alloc = serf_bucket_allocator_create(sess->pool, @@ -2150,13 +2151,17 @@ open_connection_if_needed(svn_ra_serf__session_t * sess->conns[cur]->last_status_code = -1; sess->conns[cur]->ssl_context = NULL; sess->conns[cur]->session = sess; - sess->conns[cur]->conn = serf_connection_create(sess->context, - sess->conns[cur]->address, - svn_ra_serf__conn_setup, - sess->conns[cur], - svn_ra_serf__conn_closed, - sess->conns[cur], - sess->pool); + status = serf_connection_create2(&sess->conns[cur]->conn, + sess->context, + sess->repos_url, + svn_ra_serf__conn_setup, + sess->conns[cur], + svn_ra_serf__conn_closed, + sess->conns[cur], + sess->pool); + if (status) + return svn_error_wrap_apr(status, NULL); + sess->num_conns++; /* Authentication protocol specific initalization. */ @@ -2166,6 +2171,8 @@ open_connection_if_needed(svn_ra_serf__session_t * sess->proxy_auth_protocol->init_conn_func(sess, sess->conns[cur], sess->pool); } + + return SVN_NO_ERROR; } static svn_error_t * @@ -2220,7 +2227,7 @@ finish_report(void *report_baton, svn_ra_serf__request_create(handler); /* Open the first extra connection. */ - open_connection_if_needed(sess, 0); + SVN_ERR(open_connection_if_needed(sess, 0)); sess->cur_conn = 1; closed_root = FALSE; @@ -2244,8 +2251,8 @@ finish_report(void *report_baton, /* Open extra connections if we have enough requests to send. */ if (sess->num_conns < MAX_NR_OF_CONNS) - open_connection_if_needed(sess, report->active_fetches + - report->active_propfinds); + SVN_ERR(open_connection_if_needed(sess, report->active_fetches + + report->active_propfinds)); /* Switch our connection. */ if (!report->done) Index: subversion/libsvn_ra_serf/ra_serf.h =================================================================== --- subversion/libsvn_ra_serf/ra_serf.h (revision 40357) +++ subversion/libsvn_ra_serf/ra_serf.h (working copy) @@ -1402,6 +1402,16 @@ svn_ra_serf__get_deleted_rev(svn_ra_session_t *ses /*** Authentication handler declarations ***/ /** + * Callback function that loads the credentials for Basic and Digest + * authentications, both for server and proxy authentication. + */ +apr_status_t +svn_ra_serf__credentials_callback(char **username, char **password, + serf_request_t *request, void *baton, + int code, const char *authn_type, + const char *realm, + apr_pool_t *pool); +/** * For each authentication protocol we need a handler function of type * svn_serf__auth_handler_func_t. This function will be called when an * authentication challenge is received in a session. Index: serf_private.h =================================================================== --- serf_private.h (revision 1280) +++ serf_private.h (working copy) @@ -1,9 +1,9 @@ -/* Copyright 2002-2004 Justin Erenkrantz and Greg Stein + /* Copyright 2002-2004 Justin Erenkrantz and Greg Stein * - * Licensed under the Apache License, Version 2.0 (the "License"); + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software @@ -30,6 +30,8 @@ #define SERF_IO_CONN (2) #define SERF_IO_LISTENER (3) +typedef struct serf__authn_scheme_t serf__authn_scheme_t; + typedef struct serf_io_baton_t { int type; union { @@ -62,6 +64,8 @@ struct serf_request_t { serf_bucket_t *resp_bkt; + int written; + struct serf_request_t *next; }; @@ -70,6 +74,14 @@ typedef struct serf_pollset_t { apr_pollset_t *pollset; } serf_pollset_t; +typedef struct serf__authn_info_t { + const char *realm; + + const serf__authn_scheme_t *scheme; + + void *baton; +} serf__authn_info_t; + struct serf_context_t { /* the pool used for self and for other allocations */ apr_pool_t *pool; @@ -93,6 +105,15 @@ struct serf_context_t { void *progress_baton; apr_off_t progress_read; apr_off_t progress_written; + + /* authentication info for this context, shared by all connections. */ + serf__authn_info_t authn_info; + serf__authn_info_t proxy_authn_info; + + /* List of authn types supported by the client.*/ + int authn_types; + /* Callback function used to get credentials for a realm. */ + serf_credentials_callback_t cred_cb; }; struct serf_listener_t { @@ -184,9 +205,96 @@ struct serf_connection_t { /* Host info. */ const char *host_url; apr_uri_t host_info; + + /* connection and authentication scheme specific information */ + void *authn_baton; + void *proxy_authn_baton; }; +/*** Authentication handler declarations ***/ +/** + * For each authentication scheme we need a handler function of type + * serf__auth_handler_func_t. This function will be called when an + * authentication challenge is received in a session. + */ +typedef apr_status_t +(*serf__auth_handler_func_t)(int code, + serf_request_t *request, + serf_bucket_t *response, + const char *auth_hdr, + const char *auth_attr, + void *baton, + apr_pool_t *pool); + +/** + * For each authentication scheme we need an initialization function of type + * serf__init_conn_func_t. This function will be called when a new + * connection is opened. + */ +typedef apr_status_t +(*serf__init_conn_func_t)(int code, + serf_connection_t *conn, + apr_pool_t *pool); + +/** + * For each authentication scheme we need a setup_request function of type + * serf__setup_request_func_t. This function will be called when a + * new serf_request_t object is created and should fill in the correct + * authentication headers (if needed). + */ +typedef apr_status_t +(*serf__setup_request_func_t)(int code, + serf_connection_t *conn, + const char *method, + const char *uri, + serf_bucket_t *hdrs_bkt); + +/** + * This function will be called when a response is received, so that the + * scheme handler can validate the Authentication related response headers + * (if needed). + */ +typedef apr_status_t +(*serf__validate_response_func_t)(serf_request_t *request, + serf_bucket_t *response, + apr_pool_t *pool); + +/** + * serf__authn_scheme_t: vtable for an authn scheme provider. + */ +struct serf__authn_scheme_t { + /* The http status code that's handled by this authentication scheme. + Normal values are 401 for server authentication and 407 for proxy + authentication */ + int code; + + /* The name of this authentication scheme. This should be a case + sensitive match of the string sent in the HTTP authentication header. */ + const char *name; + + /* Internal code used for this authn type. */ + int type; + + /* The initialization function if any; otherwise, NULL */ + serf__init_conn_func_t init_conn_func; + + /* The authentication handler function */ + serf__auth_handler_func_t handle_func; + + /* Function to set up the authentication header of a request */ + serf__setup_request_func_t setup_request_func; + + /* Function to validate the authentication header of a response */ + serf__validate_response_func_t validate_response_func; +}; + +apr_status_t serf__handle_auth_response(int *consumed_response, + serf_request_t *request, + serf_bucket_t *response, + void *baton, + apr_pool_t *pool); + /* fromt context.c */ void serf__context_progress_delta(void *progress_baton, apr_off_t read, apr_off_t written); Index: NOTICE =================================================================== --- NOTICE (revision 1280) +++ NOTICE (working copy) @@ -1,2 +1,3 @@ This product includes software developed by -The Apache Software Foundation (http://www.apache.org/). +The Apache Software Foundation (http://www.apache.org/) and +by the Subversion Corporation (http://subversion.org). Index: serf.h =================================================================== --- serf.h (revision 1280) +++ serf.h (working copy) @@ -72,6 +72,15 @@ typedef struct serf_request_t serf_request_t; */ #define SERF_ERROR_REQUEST_LOST (APR_OS_START_USERERR + SERF_ERROR_RANGE + 2) +/* General authentication related errors */ +#define SERF_ERROR_AUTHN_FAILED (APR_OS_START_USERERR + SERF_ERROR_RANGE + 90) + +/* None of the available authn mechanisms for the request are supported */ +#define SERF_ERROR_AUTHN_NOT_SUPPORTED (APR_OS_START_USERERR + SERF_ERROR_RANGE + 91) + +/* Authn was requested by the server but the header lacked some attribute */ +#define SERF_ERROR_AUTHN_MISSING_ATTRIBUTE (APR_OS_START_USERERR + SERF_ERROR_RANGE + 92) + /** * Create a new context for serf operations. * @@ -296,6 +305,14 @@ typedef apr_status_t (*serf_response_handler_t)(se apr_pool_t *pool); /** + */ +typedef apr_status_t (*serf_credentials_callback_t)(char **username, char **password, + serf_request_t *request, void *baton, + int code, const char *authn_type, + const char *realm, + apr_pool_t *pool); + +/** * Create a new connection associated with the @a ctx serf context. * * A connection will be created to (eventually) connect to the address @@ -536,6 +553,22 @@ SERF_DECLARE(void) serf_config_proxy( serf_context_t *ctx, apr_sockaddr_t *address); +/* Supported authentication types. */ +#define SERF_AUTHN_NONE 0x00 +#define SERF_AUTHN_BASIC 0x01 +#define SERF_AUTHN_DIGEST 0x02 +#define SERF_AUTHN_NTLM 0x04 +#define SERF_AUTHN_NEGOTIATE 0x08 +#define SERF_AUTHN_ALL 0xFF + +SERF_DECLARE(void) serf_config_authn_types( + serf_context_t *ctx, + int authn_types); + +SERF_DECLARE(void) serf_config_credentials_callback( + serf_context_t *ctx, + serf_credentials_callback_t cred_cb); + /* ### maybe some connection control functions for flood? */ /*** Special bucket creation functions ***/ Index: serfmake =================================================================== --- serfmake (revision 1280) +++ serfmake (working copy) @@ -34,6 +34,8 @@ LIB_FILES = [ ('buckets', 'ssl_buckets'), ('buckets', 'barrier_buckets'), ('buckets', 'chunk_buckets'), + ('auth', 'auth'), + ('auth', 'auth_basic'), ] TEST_DEPS = [ @@ -252,7 +254,7 @@ class Builder: self._add_dep(prog, [lib, obj], cmd) def _add_compile(self, src, obj, hdrs): - cmd = '%s --silent --mode=compile %s %s %s %s -c -o %s %s' % ( + cmd = '%s --silent --mode=compile %s -g %s %s %s -c -o %s %s' % ( self.LIBTOOL, self.CC, self.CFLAGS, self.CPPFLAGS, self.INCLUDES, obj.fname, src.fname) self._add_dep(obj, [src] + hdrs, cmd) Index: auth/auth.c =================================================================== --- auth/auth.c (revision 0) +++ auth/auth.c (revision 0) @@ -0,0 +1,341 @@ +/* Copyright 2009 Justin Erenkrantz and Greg Stein + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "serf.h" +#include "serf_private.h" +#include "auth.h" + +#include <apr.h> +#include <apr_strings.h> + +static apr_status_t +default_auth_response_handler(serf_request_t *request, + serf_bucket_t *response, + apr_pool_t *pool) +{ + return APR_SUCCESS; +} + +static const serf__authn_scheme_t serf_authn_schemes[] = { + { + 401, + "Basic", + SERF_AUTHN_BASIC, + serf__init_basic_connection, + serf__handle_basic_auth, + serf__setup_request_basic_auth, + default_auth_response_handler, + }, +/* { + 407, + "Basic", + SERF_AUTHN_BASIC, + serf__init_proxy_basic_connection, + serf__handle_proxy_basic_auth, + serf__setup_request_proxy_basic_auth, + default_auth_response_handler, + }, + { + 401, + "Digest", + SERF_AUTHN_DIGEST, + serf__init_digest_connection, + serf__handle_digest_auth, + serf__setup_request_digest_auth, + serf__validate_response_digest_auth, + },*/ + + /* ADD NEW AUTHENTICATION IMPLEMENTATIONS HERE (as they're written) */ + + /* sentinel */ + { 0 } +}; + + +/** + * Baton passed to the response header callback function + */ +typedef struct { + int code; + apr_status_t status; + const char *header; + serf_request_t *request; + serf_bucket_t *response; + void *baton; + apr_pool_t *pool; + const serf__authn_scheme_t *scheme; + const char *last_scheme_name; +} auth_baton_t; + +/* Reads and discards all bytes in the response body. */ +static apr_status_t discard_body(serf_bucket_t *response) +{ + apr_status_t status; + const char *data; + apr_size_t len; + + while (1) { + status = serf_bucket_read(response, SERF_READ_ALL_AVAIL, &data, &len); + + if (status) { + return status; + } + + /* feed me */ + } +} + +/** + * handle_auth_header is called for each header in the response. It filters + * out the Authenticate headers (WWW or Proxy depending on what's needed) and + * tries to find a matching scheme handler. + * + * Returns a non-0 value of a matching handler was found. + */ +static int handle_auth_header(void *baton, + const char *key, + const char *header) +{ + auth_baton_t *ab = baton; + int scheme_found = FALSE; + const char *auth_name; + const char *auth_attr; + const serf__authn_scheme_t *scheme = NULL; + serf_connection_t *conn = ab->request->conn; + serf_context_t *ctx = conn->ctx; + + /* We're only interested in xxxx-Authenticate headers. */ + if (strcmp(key, ab->header) != 0) + return 0; + + /* Extract the authentication scheme name, and prepare for reading + the attributes. */ + auth_attr = strchr(header, ' '); + if (auth_attr) { + auth_name = apr_pstrmemdup(ab->pool, header, auth_attr - header); + ++auth_attr; + } + else + auth_name = header; + + ab->last_scheme_name = auth_name; + + /* Find the matching authentication handler. + Note that we don't reuse the auth scheme stored in the session, + as that may have changed. (ex. fallback from ntlm to basic.) */ + for (scheme = serf_authn_schemes; scheme->code != 0; ++scheme) { + if (ab->code == scheme->code && + strcmp(auth_name, scheme->name) == 0 && + ctx->authn_types & scheme->type) { + serf__auth_handler_func_t handler = scheme->handle_func; + apr_status_t status; + + /* If this is the first time we use this scheme in this context, + make sure to initialize the authentication part of the context + first. */ + if (ab->code == 401 && ctx->authn_info.scheme != scheme) { + status = scheme->init_conn_func(ab->code, conn, ctx->pool); + if (!status) + ctx->authn_info.scheme = scheme; + else + ctx->authn_info.scheme = NULL; + } + else if (ab->code == 407 && ctx->proxy_authn_info.scheme != scheme) { + status = scheme->init_conn_func(ab->code, conn, ctx->pool); + if (!status) + ctx->proxy_authn_info.scheme = scheme; + else + ctx->proxy_authn_info.scheme = NULL; + } + + if (!status) { + scheme_found = TRUE; + ab->scheme = scheme; + status = handler(ab->code, ab->request, ab->response, + header, auth_attr, ab->baton, ctx->pool); + } + + /* If the authentication fails, cache the error for now. Try the + next available scheme. If there's none raise the error. */ + if (status) { + scheme_found = FALSE; + scheme = NULL; + ab->status = status; + } + + break; + } + } + + /* If a matching scheme handler was found, we can stop iterating + over the response headers - so return a non-0 value. */ + return scheme_found; +} + +/* Dispatch authentication handling. This function matches the possible + authentication mechanisms with those available. Server and proxy + authentication are evaluated separately. */ +static apr_status_t dispatch_auth(int code, + serf_request_t *request, + serf_bucket_t *response, + void *baton, + apr_pool_t *pool) +{ + serf_context_t *ctx = request->conn->ctx; + serf_bucket_t *hdrs; + serf__authn_info_t authn_info = (code == 401 ? ctx->authn_info : + ctx->proxy_authn_info); + + if (code == 401 || code == 407) { + auth_baton_t ab = { 0 }; + const char *auth_hdr; + apr_status_t status; + + ab.code = code; + ab.status = APR_SUCCESS; + ab.request = request; + ab.response = response; + ab.baton = baton; + ab.pool = pool; + + /* Before iterating over all authn headers, check if there are any. */ + if (code == 401) + ab.header = "WWW-Authenticate"; + else + ab.header = "Proxy-Authenticate"; + + hdrs = serf_bucket_response_get_headers(response); + auth_hdr = serf_bucket_headers_get(hdrs, ab.header); + + if (!auth_hdr) { + return SERF_ERROR_AUTHN_FAILED; + } + + /* Iterate over all headers. Try to find a matching authentication scheme + handler. + + Note: it is possible to have multiple Authentication: headers. We do + not want to combine them (per normal header combination rules) as that + would make it hard to parse. Instead, we want to individually parse + and handle each header in the response, looking for one that we can + work with. + */ + serf_bucket_headers_do(hdrs, + handle_auth_header, + &ab); + if (ab.status != APR_SUCCESS) + return ab.status; + + if (!ab.scheme || ab.scheme->name == NULL) { + /* No matching authentication found. */ + return SERF_ERROR_AUTHN_NOT_SUPPORTED; + } + } else { + /* Validate the response authn headers if needed. */ + + } + + return APR_SUCCESS; +} + +/* Read the headers of the response and try the available + handlers if authentication or validation is needed. */ +apr_status_t serf__handle_auth_response(int *consumed_response, + serf_request_t *request, + serf_bucket_t *response, + void *baton, + apr_pool_t *pool) +{ + apr_status_t status; + serf_status_line sl; + + *consumed_response = 0; + + status = serf_bucket_response_status(response, &sl); + if (SERF_BUCKET_READ_ERROR(status)) { + return status; + } + if (!sl.version && (APR_STATUS_IS_EOF(status) || + APR_STATUS_IS_EAGAIN(status))) { + return status; + } + + status = serf_bucket_response_wait_for_headers(response); + if (status) { + if (!APR_STATUS_IS_EOF(status)) { + return status; + } + + /* If status is APR_EOF, there were no headers to read. + This can be ok in some situations, and it definitely + means there's no authentication requested now. */ + return APR_SUCCESS; + } + + /* Don't bother handling the authentication request if the response + wasn't received completely yet. Serf will call serf__handle_auth_response + again when more data is received. */ + if (! APR_STATUS_IS_EAGAIN(status)) { + status = dispatch_auth(sl.code, request, response, baton, pool); + + if (status != APR_SUCCESS) { + return status; + } + + if (sl.code == 401 || sl.code == 407) { + /* Authentication requested. */ + status = discard_body(response); + *consumed_response = 1; + + /* Requeue the request with the necessary auth headers. */ + /* ### Application doesn't know about this request! */ + serf_connection_priority_request_create(request->conn, + request->setup, + request->setup_baton); + + return status; + } + + return APR_SUCCESS; + } + + return status; +} + +/** + * base64 encode the authentication data and build an authentication + * header in this format: + * [SCHEME] [BASE64 of auth DATA] + */ +void serf__encode_auth_header(const char **header, + const char *scheme, + const char *data, apr_size_t data_len, + apr_pool_t *pool) +{ + apr_size_t encoded_len, scheme_len; + char *ptr; + + encoded_len = apr_base64_encode_len(data_len); + scheme_len = strlen(scheme); + + ptr = apr_palloc(pool, encoded_len + scheme_len + 1); + *header = ptr; + + apr_cpystrn(ptr, scheme, scheme_len + 1); + ptr += scheme_len; + *ptr++ = ' '; + + apr_base64_encode(ptr, data, data_len); +} Index: auth/auth_basic.c =================================================================== --- auth/auth_basic.c (revision 0) +++ auth/auth_basic.c (revision 0) @@ -0,0 +1,145 @@ +/* Copyright 2009 Justin Erenkrantz and Greg Stein + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*** Basic authentication ***/ + +#include <serf.h> +#include <serf_private.h> +#include <auth/auth.h> + +#include <apr.h> +#include <apr_base64.h> +#include <apr_strings.h> + +typedef struct basic_authn_info_t { + const char *header; + const char *value; +} basic_authn_info_t; + +apr_status_t +serf__handle_basic_auth(int code, + serf_request_t *request, + serf_bucket_t *response, + const char *auth_hdr, + const char *auth_attr, + void *baton, + apr_pool_t *pool) +{ + const char *tmp; + apr_size_t tmp_len; + serf_connection_t *conn = request->conn; + serf_context_t *ctx = conn->ctx; + basic_authn_info_t *basic_info = (code == 401) ? conn->authn_baton : + conn->proxy_authn_baton; + serf__authn_info_t *authn_info = (code == 401) ? &ctx->authn_info : + &ctx->proxy_authn_info; + apr_status_t status; + apr_pool_t *cred_pool; + char *username, *password; + + /* Can't do Basic authentication if there's no callback to get + username & password. */ + if (!ctx->cred_cb) { + return SERF_ERROR_AUTHN_FAILED; + } + + if (!authn_info->realm) { + char *realm_name = NULL; + const char *eq = strchr(auth_attr, '='); + apr_port_t port; + + if (eq && strncasecmp(auth_attr, "realm", 5) == 0) { + realm_name = apr_pstrdup(pool, eq + 1); + if (realm_name[0] == '\"') { + apr_size_t realm_len; + + realm_len = strlen(realm_name); + if (realm_name[realm_len - 1] == '\"') { + realm_name[realm_len - 1] = '\0'; + realm_name++; + } + } + } + + if (!realm_name) { + return SERF_ERROR_AUTHN_MISSING_ATTRIBUTE; + } + + authn_info->realm = apr_psprintf(conn->pool, "<%s://%s:%d> %s", + conn->host_info.scheme, + conn->host_info.hostname, + conn->host_info.port, + realm_name); + } + + /* Ask the application for credentials */ + apr_pool_create(&cred_pool, pool); + status = (*ctx->cred_cb)(&username, &password, request, baton, + code, authn_info->scheme->name, + authn_info->realm, cred_pool); + if (status) { + apr_pool_destroy(cred_pool); + return status; + } + + tmp = apr_pstrcat(conn->pool, username, ":", password, NULL); + tmp_len = strlen(tmp); + apr_pool_destroy(cred_pool); + + serf__encode_auth_header(&basic_info->value, + authn_info->scheme->name, + tmp, tmp_len, pool); + basic_info->header = (code == 401) ? "Authorization" : "Proxy-Authorization"; + + return APR_SUCCESS; +} + +apr_status_t +serf__init_basic_connection(int code, + serf_connection_t *conn, + apr_pool_t *pool) +{ + if (code == 401) { + conn->authn_baton = apr_pcalloc(pool, sizeof(basic_authn_info_t)); + } else { + conn->proxy_authn_baton = apr_pcalloc(pool, sizeof(basic_authn_info_t)); + } + + return APR_SUCCESS; +} + +apr_status_t +serf__setup_request_basic_auth(int code, + serf_connection_t *conn, + const char *method, + const char *uri, + serf_bucket_t *hdrs_bkt) +{ + serf_context_t *ctx = conn->ctx; + basic_authn_info_t *authn_info; + + if (code == 401) { + authn_info = conn->authn_baton; + } else { + authn_info = conn->proxy_authn_baton; + } + + if (authn_info && authn_info->header && authn_info->value) { + serf_bucket_headers_setn(hdrs_bkt, authn_info->header, authn_info->value); + return APR_SUCCESS; + } + + return SERF_ERROR_AUTHN_FAILED; +} Index: auth/auth.h =================================================================== --- auth/auth.h (revision 0) +++ auth/auth.h (revision 0) @@ -0,0 +1,49 @@ +/* Copyright 2009 Justin Erenkrantz and Greg Stein + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef AUTH_H +#define AUTH_H + +#ifdef __cplusplus +extern "C" { +#endif + +void serf__encode_auth_header(const char **header, const char *protocol, + const char *data, apr_size_t data_len, + apr_pool_t *pool); + +apr_status_t serf__handle_basic_auth(int code, + serf_request_t *request, + serf_bucket_t *response, + const char *auth_hdr, + const char *auth_attr, + void *baton, + apr_pool_t *pool); + +apr_status_t serf__init_basic_connection(int code, + serf_connection_t *conn, + apr_pool_t *pool); + +apr_status_t serf__setup_request_basic_auth(int code, + serf_connection_t *conn, + const char *method, + const char *uri, + serf_bucket_t *hdrs_bkt); + +#ifdef __cplusplus +} +#endif + +#endif /* !AUTH_H */ Index: context.c =================================================================== --- context.c (revision 1280) +++ context.c (working copy) @@ -102,6 +102,18 @@ SERF_DECLARE(void) serf_config_proxy(serf_context_ ctx->proxy_address = address; } +SERF_DECLARE(void) serf_config_credentials_callback(serf_context_t *ctx, + serf_credentials_callback_t cred_cb) +{ + ctx->cred_cb = cred_cb; +} + +SERF_DECLARE(void) serf_config_authn_types(serf_context_t *ctx, + int authn_types) +{ + ctx->authn_types = authn_types; +} + SERF_DECLARE(serf_context_t *) serf_context_create_ex(void *user_baton, serf_socket_add_t addf, serf_socket_remove_t rmf, @@ -132,6 +144,8 @@ SERF_DECLARE(serf_context_t *) serf_context_create ctx->progress_read = 0; ctx->progress_written = 0; + ctx->authn_types = SERF_AUTHN_ALL; + return ctx; } Index: outgoing.c =================================================================== --- outgoing.c (revision 1281) +++ outgoing.c (working copy) @@ -107,7 +107,7 @@ apr_status_t serf__conn_update_pollset(serf_connec } else { while (request != NULL && request->req_bkt == NULL && - request->setup == NULL) + request->written) request = request->next; if (request != NULL) desc.reqevents |= APR_POLLOUT; @@ -344,6 +344,20 @@ static void destroy_ostream(serf_connection_t *con } } + +/* A socket was closed, inform the application. */ +static void handle_conn_closed(serf_connection_t *conn, apr_status_t status) +{ + (*conn->closed)(conn, conn->closed_baton, status, + conn->pool); + + /* Restart the authentication phase on this new connection. */ + if (conn->ctx->authn_info.scheme) { + conn->ctx->authn_info.scheme->init_conn_func(401, conn, + conn->ctx->pool); + } +} + static apr_status_t reset_connection(serf_connection_t *conn, int requeue_requests) { @@ -372,7 +386,7 @@ static apr_status_t reset_connection(serf_connecti /* If we haven't started to write the connection, bring it over * unchanged to our new socket. Otherwise, call the cancel function. */ - if (requeue_requests && old_reqs->setup) { + if (requeue_requests && !old_reqs->written) { serf_request_t *req = old_reqs; old_reqs = old_reqs->next; req->next = NULL; @@ -397,8 +411,7 @@ static apr_status_t reset_connection(serf_connecti remove_connection(ctx, conn); status = apr_socket_close(conn->skt); if (conn->closed != NULL) { - (*conn->closed)(conn, conn->closed_baton, status, - conn->pool); + handle_conn_closed(conn, status); } conn->skt = NULL; } @@ -517,7 +530,7 @@ static apr_status_t write_to_connection(serf_conne /* Find a request that has data which needs to be delivered. */ while (request != NULL && - request->req_bkt == NULL && request->setup == NULL) + request->req_bkt == NULL && request->written) request = request->next; /* assert: request != NULL || conn->vec_len */ @@ -559,7 +572,7 @@ static apr_status_t write_to_connection(serf_conne * to write. */ while (request != NULL && - request->req_bkt == NULL && request->setup == NULL) + request->req_bkt == NULL && request->written) request = request->next; if (request == NULL) { @@ -604,7 +617,7 @@ static apr_status_t write_to_connection(serf_conne return read_status; } - request->setup = NULL; + request->written = 1; serf_bucket_aggregate_append(conn->ostream_tail, request->req_bkt); } @@ -687,12 +700,32 @@ static apr_status_t handle_response(serf_request_t apr_pool_t *pool) { apr_status_t status; + int consumed_response = 0; - status = (*request->handler)(request, - request->resp_bkt, - request->handler_baton, - pool); + status = serf__handle_auth_response(&consumed_response, + request, + request->resp_bkt, + request->handler_baton, + pool); + /* If there was an error reading the response (maybe there wasn't + enough data available), don't bother passing the response to the + application. + + If the authentication was tried, but failed, pass the response + to the application, maybe it can do better. */ + if (APR_STATUS_IS_EOF(status) || + APR_STATUS_IS_EAGAIN(status)) { + return status; + } + + if (!consumed_response) { + return (*request->handler)(request, + request->resp_bkt, + request->handler_baton, + pool); + } + return status; } @@ -704,8 +737,7 @@ static apr_status_t read_from_connection(serf_conn int close_connection = FALSE; /* Whatever is coming in on the socket corresponds to the first request - * on our chain. - */ + on our chain. */ serf_request_t *request = conn->requests; /* assert: request != NULL */ @@ -743,7 +775,7 @@ static apr_status_t read_from_connection(serf_conn * If we see an EOF (due to an expired timeout), we'll reset the * connection and open a new one. */ - if (request->req_bkt || request->setup) { + if (request->req_bkt || !request->written) { const char *data; apr_size_t len; @@ -861,7 +893,7 @@ static apr_status_t read_from_connection(serf_conn * update the pollset. We don't want to read from this socket any * more. We are definitely done with this loop, too. */ - if (request == NULL || request->setup) { + if (request == NULL || !request->written) { conn->dirty_conn = 1; conn->ctx->dirty_pollset = 1; status = APR_SUCCESS; @@ -1016,8 +1048,7 @@ SERF_DECLARE(apr_status_t) serf_connection_close( remove_connection(ctx, conn); status = apr_socket_close(conn->skt); if (conn->closed != NULL) { - (*conn->closed)(conn, conn->closed_baton, status, - conn->pool); + handle_conn_closed(conn, status); } conn->skt = NULL; } @@ -1070,6 +1101,7 @@ SERF_DECLARE(serf_request_t *) serf_connection_req request->respool = NULL; request->req_bkt = NULL; request->resp_bkt = NULL; + request->written = 0; request->next = NULL; /* Link the request to the end of the request chain. */ @@ -1103,6 +1135,7 @@ SERF_DECLARE(serf_request_t *) serf_connection_pri request->respool = NULL; request->req_bkt = NULL; request->resp_bkt = NULL; + request->written = 0; request->next = NULL; /* Link the new request after the last written request, but before all @@ -1116,7 +1149,7 @@ SERF_DECLARE(serf_request_t *) serf_connection_pri prev = NULL; /* Find a request that has data which needs to be delivered. */ - while (iter != NULL && iter->req_bkt == NULL && iter->setup == NULL) { + while (iter != NULL && iter->req_bkt == NULL && iter->written) { prev = iter; iter = iter->next; } @@ -1182,17 +1215,29 @@ SERF_DECLARE(serf_bucket_t *) serf_request_bucket_ serf_bucket_alloc_t *allocator) { serf_bucket_t *req_bkt, *hdrs_bkt; + serf_connection_t *conn = request->conn; + serf_context_t *ctx = conn->ctx; req_bkt = serf_bucket_request_create(method, uri, body, allocator); hdrs_bkt = serf_bucket_request_get_headers(req_bkt); /* Proxy? */ - if (request->conn->ctx->proxy_address && request->conn->host_url) - serf_bucket_request_set_root(req_bkt, request->conn->host_url); + if (ctx->proxy_address && conn->host_url) + serf_bucket_request_set_root(req_bkt, conn->host_url); - if (request->conn->host_info.hostname) + if (conn->host_info.hostname) serf_bucket_headers_setn(hdrs_bkt, "Host", - request->conn->host_info.hostname); + conn->host_info.hostname); + /* Setup server authorization headers */ + if (ctx->authn_info.scheme) + ctx->authn_info.scheme->setup_request_func(401, conn, method, uri, + hdrs_bkt); + + /* Setup proxy authorization headers */ + if (ctx->proxy_authn_info.scheme) + ctx->proxy_authn_info.scheme->setup_request_func(407, conn, method, + uri, hdrs_bkt); + return req_bkt; } |
|
|
Re: Moving ra_serf authn handlers to serf.2009-11-03 23:19:30 Lieven Govaerts napisał(a):
> As already mentioned at SubConf last week, I'm moving the code that > handles the authentication schemes in ra_serf from Subversion to Serf. > > The main goal for me is to be able to add features to serf that rely > on authentication being in serf too (e.g. SSL tunneling over HTTP > proxies). It also just seems more logical to do the authentication in > the http library instead of the application, and this might help other > users of serf. > > The code I want to move is parts of libsvn_ra_serf/util.c and all of > the files libsvn_ra_serf/(auth.*|auth_digest.*|auth_kerb.*|win32_auth_sspi.*). > Most of these have been written by Justin and me, but there were some > fixes, cleanups, buildscripts by other svn committers. > > Both Subversion and Serf are covered by the Apache License 2.0, so I > don't think there's a problem in this area. > > However, people have suggested to add a line in serf/NOTICE that says > Serf includes code developed by the Subversion Corporation. > > Attached my WIP patches for serf and subversion, including the changed > NOTICE file. > > Thoughts? Remarks? > Index: serfmake > =================================================================== > --- serfmake (revision 1280) > +++ serfmake (working copy) > ... > TEST_DEPS = [ > @@ -252,7 +254,7 @@ class Builder: > self._add_dep(prog, [lib, obj], cmd) > > def _add_compile(self, src, obj, hdrs): > - cmd = '%s --silent --mode=compile %s %s %s %s -c -o %s %s' % ( > + cmd = '%s --silent --mode=compile %s -g %s %s %s -c -o %s %s' % ( Please revert this change before committing :) . > self.LIBTOOL, self.CC, self.CFLAGS, self.CPPFLAGS, self.INCLUDES, > obj.fname, src.fname) > self._add_dep(obj, [src] + hdrs, cmd) > -- Arfrever Frehtes Taifersar Arahesis ------------------------------------------------------ http://subversion.tigris.org/ds/viewMessage.do?dsForumId=462&dsMessageId=2414537 |
|
|
Re: [serf-dev] Re: Moving ra_serf authn handlers to serf.On Wed, Nov 4, 2009 at 11:44 PM, Arfrever Frehtes Taifersar Arahesis
<arfrever.fta@...> wrote: > > 2009-11-03 23:19:30 Lieven Govaerts napisał(a): >> As already mentioned at SubConf last week, I'm moving the code that >> handles the authentication schemes in ra_serf from Subversion to Serf. >> >> The main goal for me is to be able to add features to serf that rely >> on authentication being in serf too (e.g. SSL tunneling over HTTP >> proxies). It also just seems more logical to do the authentication in >> the http library instead of the application, and this might help other >> users of serf. >> >> The code I want to move is parts of libsvn_ra_serf/util.c and all of >> the files libsvn_ra_serf/(auth.*|auth_digest.*|auth_kerb.*|win32_auth_sspi.*). >> Most of these have been written by Justin and me, but there were some >> fixes, cleanups, buildscripts by other svn committers. >> >> Both Subversion and Serf are covered by the Apache License 2.0, so I >> don't think there's a problem in this area. >> >> However, people have suggested to add a line in serf/NOTICE that says >> Serf includes code developed by the Subversion Corporation. >> >> Attached my WIP patches for serf and subversion, including the changed >> NOTICE file. >> >> Thoughts? Remarks? > >> Index: serfmake >> =================================================================== >> --- serfmake (revision 1280) >> +++ serfmake (working copy) >> ... >> TEST_DEPS = [ >> @@ -252,7 +254,7 @@ class Builder: >> self._add_dep(prog, [lib, obj], cmd) >> >> def _add_compile(self, src, obj, hdrs): >> - cmd = '%s --silent --mode=compile %s %s %s %s -c -o %s %s' % ( >> + cmd = '%s --silent --mode=compile %s -g %s %s %s -c -o %s %s' % ( > > Please revert this change before committing :) . > He, yes I will, but thanks for the reminder. Actually, I'm going to add a flag in serfmake that allows me to make a debug build. Lieven ------------------------------------------------------ http://subversion.tigris.org/ds/viewMessage.do?dsForumId=462&dsMessageId=2414539 |
|
|
Re: Moving ra_serf authn handlers to serf.Woohoo! Go for it! -- justin
On Tuesday, November 3, 2009, Lieven Govaerts <svnlgo@...> wrote: > As already mentioned at SubConf last week, I'm moving the code that > handles the authentication schemes in ra_serf from Subversion to Serf. > > The main goal for me is to be able to add features to serf that rely > on authentication being in serf too (e.g. SSL tunneling over HTTP > proxies). It also just seems more logical to do the authentication in > the http library instead of the application, and this might help other > users of serf. > > The code I want to move is parts of libsvn_ra_serf/util.c and all of > the files libsvn_ra_serf/(auth.*|auth_digest.*|auth_kerb.*|win32_auth_sspi.*). > Most of these have been written by Justin and me, but there were some > fixes, cleanups, buildscripts by other svn committers. > > Both Subversion and Serf are covered by the Apache License 2.0, so I > don't think there's a problem in this area. > > However, people have suggested to add a line in serf/NOTICE that says > Serf includes code developed by the Subversion Corporation. > > Attached my WIP patches for serf and subversion, including the changed > NOTICE file. > > Thoughts? Remarks? > > Lieven > > ------------------------------------------------------ > http://subversion.tigris.org/ds/viewMessage.do?dsForumId=462&dsMessageId=2414228 ------------------------------------------------------ http://subversion.tigris.org/ds/viewMessage.do?dsForumId=462&dsMessageId=2414557 |
|
|
Re: Moving ra_serf authn handlers to serf.On Wed, Nov 4, 2009 at 1:19 AM, Lieven Govaerts <svnlgo@...> wrote:
> As already mentioned at SubConf last week, I'm moving the code that > handles the authentication schemes in ra_serf from Subversion to Serf. > > The main goal for me is to be able to add features to serf that rely > on authentication being in serf too (e.g. SSL tunneling over HTTP > proxies). It also just seems more logical to do the authentication in > the http library instead of the application, and this might help other > users of serf. > > The code I want to move is parts of libsvn_ra_serf/util.c and all of > the files libsvn_ra_serf/(auth.*|auth_digest.*|auth_kerb.*|win32_auth_sspi.*). > Most of these have been written by Justin and me, but there were some > fixes, cleanups, buildscripts by other svn committers. > -- Ivan Zhakov VisualSVN Team ------------------------------------------------------ http://subversion.tigris.org/ds/viewMessage.do?dsForumId=462&dsMessageId=2414633 |
|
|
Re: Moving ra_serf authn handlers to serf.On Thu, Nov 5, 2009 at 11:11 AM, Ivan Zhakov <ivan@...> wrote:
> On Wed, Nov 4, 2009 at 1:19 AM, Lieven Govaerts <svnlgo@...> wrote: >> As already mentioned at SubConf last week, I'm moving the code that >> handles the authentication schemes in ra_serf from Subversion to Serf. >> >> The main goal for me is to be able to add features to serf that rely >> on authentication being in serf too (e.g. SSL tunneling over HTTP >> proxies). It also just seems more logical to do the authentication in >> the http library instead of the application, and this might help other >> users of serf. >> >> The code I want to move is parts of libsvn_ra_serf/util.c and all of >> the files libsvn_ra_serf/(auth.*|auth_digest.*|auth_kerb.*|win32_auth_sspi.*). >> Most of these have been written by Justin and me, but there were some >> fixes, cleanups, buildscripts by other svn committers. >> > I'd like this idea! > Any updates on this? I'm considering several fixes to win32_auth_sspi and I'd like to commit it directly to serf, instead of libsvn_ra_serf. -- Ivan Zhakov VisualSVN Team ------------------------------------------------------ http://subversion.tigris.org/ds/viewMessage.do?dsForumId=462&dsMessageId=2425356 Please start new threads on the <dev@...> mailing list. To subscribe to the new list, send an empty e-mail to <dev-subscribe@...>. |
|
|
Re: Moving ra_serf authn handlers to serf.Ivan,
On Mon, Nov 30, 2009 at 8:20 AM, Ivan Zhakov <ivan@...> wrote: > On Thu, Nov 5, 2009 at 11:11 AM, Ivan Zhakov <ivan@...> wrote: >> On Wed, Nov 4, 2009 at 1:19 AM, Lieven Govaerts <svnlgo@...> wrote: >>> As already mentioned at SubConf last week, I'm moving the code that >>> handles the authentication schemes in ra_serf from Subversion to Serf. >>> >>> The main goal for me is to be able to add features to serf that rely >>> on authentication being in serf too (e.g. SSL tunneling over HTTP >>> proxies). It also just seems more logical to do the authentication in >>> the http library instead of the application, and this might help other >>> users of serf. >>> >>> The code I want to move is parts of libsvn_ra_serf/util.c and all of >>> the files libsvn_ra_serf/(auth.*|auth_digest.*|auth_kerb.*|win32_auth_sspi.*). >>> Most of these have been written by Justin and me, but there were some >>> fixes, cleanups, buildscripts by other svn committers. >>> >> I'd like this idea! >> > Hi Lieven, > > Any updates on this? I'm considering several fixes to win32_auth_sspi > and I'd like to commit it directly to serf, instead of libsvn_ra_serf. > I won't have time to continue working on this before ~20/12. In a first stage I'll migrate basic and digest authn, as these don't require changes to the build system and don't require me having a Windows dev. environment. Do you have an environment to test NTLM & Kerberos authentication? Lieven ------------------------------------------------------ http://subversion.tigris.org/ds/viewMessage.do?dsForumId=462&dsMessageId=2425391 Please start new threads on the <dev@...> mailing list. To subscribe to the new list, send an empty e-mail to <dev-subscribe@...>. |
| Free embeddable forum powered by Nabble | Forum Help |