Reorganize the output to "svnserve --help".
[svn.git] / subversion / libsvn_ra_serf / auth.c
blobb04309a7e04a40558d3e45cedef6db7867ead805
1 /* auth.c: ra_serf authentication handling
3 * ====================================================================
4 * Copyright (c) 2007 CollabNet. All rights reserved.
6 * This software is licensed as described in the file COPYING, which
7 * you should have received as part of this distribution. The terms
8 * are also available at http://subversion.tigris.org/license-1.html.
9 * If newer versions of this license are posted there, you may use a
10 * newer version instead, at your option.
12 * This software consists of voluntary contributions made by many
13 * individuals. For exact contribution history, see the revision
14 * history and logs, available at http://subversion.tigris.org/.
15 * ====================================================================
18 /*** Includes. ***/
20 #include <serf.h>
21 #include <apr_base64.h>
23 #include "ra_serf.h"
24 #include "win32_auth_sspi.h"
25 #include "svn_private_config.h"
27 /*** Forward declarations. ***/
29 static svn_error_t *
30 handle_basic_auth(svn_ra_serf__session_t *session,
31 svn_ra_serf__connection_t *conn,
32 serf_request_t *request,
33 serf_bucket_t *response,
34 char *auth_hdr,
35 char *auth_attr,
36 apr_pool_t *pool);
38 static svn_error_t *
39 init_basic_connection(svn_ra_serf__session_t *session,
40 svn_ra_serf__connection_t *conn,
41 apr_pool_t *pool);
43 static svn_error_t *
44 setup_request_basic_auth(svn_ra_serf__connection_t *conn,
45 serf_bucket_t *hdrs_bkt);
47 static svn_error_t *
48 handle_proxy_basic_auth(svn_ra_serf__session_t *session,
49 svn_ra_serf__connection_t *conn,
50 serf_request_t *request,
51 serf_bucket_t *response,
52 char *auth_hdr,
53 char *auth_attr,
54 apr_pool_t *pool);
56 static svn_error_t *
57 init_proxy_basic_connection(svn_ra_serf__session_t *session,
58 svn_ra_serf__connection_t *conn,
59 apr_pool_t *pool);
61 static svn_error_t *
62 setup_request_proxy_basic_auth(svn_ra_serf__connection_t *conn,
63 serf_bucket_t *hdrs_bkt);
65 /*** Global variables. ***/
66 static const svn_ra_serf__auth_protocol_t serf_auth_protocols[] = {
68 401,
69 "Basic",
70 init_basic_connection,
71 handle_basic_auth,
72 setup_request_basic_auth,
75 407,
76 "Basic",
77 init_proxy_basic_connection,
78 handle_proxy_basic_auth,
79 setup_request_proxy_basic_auth,
81 #ifdef SVN_RA_SERF_SSPI_ENABLED
83 401,
84 "NTLM",
85 init_sspi_connection,
86 handle_sspi_auth,
87 setup_request_sspi_auth,
89 #endif /* SVN_RA_SERF_SSPI_ENABLED */
91 /* ADD NEW AUTHENTICATION IMPLEMENTATIONS HERE (as they're written) */
93 /* sentinel */
94 { 0 }
97 /*** Code. ***/
99 /**
100 * base64 encode the authentication data and build an authentication
101 * header in this format:
102 * [PROTOCOL] [BASE64 AUTH DATA]
104 void
105 svn_ra_serf__encode_auth_header(const char * protocol, char **header,
106 const char * data, apr_size_t data_len,
107 apr_pool_t *pool)
109 apr_size_t encoded_len, proto_len;
110 char * ptr;
112 encoded_len = apr_base64_encode_len(data_len);
113 proto_len = strlen(protocol);
115 *header = apr_palloc(pool, encoded_len + proto_len + 1);
116 ptr = *header;
118 apr_cpystrn(ptr, protocol, proto_len + 1);
119 ptr += proto_len;
120 *ptr++ = ' ';
122 apr_base64_encode(ptr, data, data_len);
126 svn_error_t *
127 svn_ra_serf__handle_auth(int code,
128 svn_ra_serf__session_t *session,
129 svn_ra_serf__connection_t *conn,
130 serf_request_t *request,
131 serf_bucket_t *response,
132 apr_pool_t *pool)
134 serf_bucket_t *hdrs;
135 const svn_ra_serf__auth_protocol_t *prot;
136 char *auth_name, *auth_attr, *auth_hdr, *header, *header_attr;
138 hdrs = serf_bucket_response_get_headers(response);
139 if (code == 401)
140 auth_hdr = (char*)serf_bucket_headers_get(hdrs, "WWW-Authenticate");
141 else if (code == 407)
142 auth_hdr = (char*)serf_bucket_headers_get(hdrs, "Proxy-Authenticate");
144 if (!auth_hdr)
146 if (session->auth_protocol)
147 return svn_error_createf(SVN_ERR_AUTHN_FAILED, NULL,
148 "%s Authentication failed",
149 session->auth_protocol->auth_name);
150 else
151 return svn_error_create(SVN_ERR_AUTHN_FAILED, NULL, NULL);
154 /* If multiple *-Authenticate headers are found, serf will combine them into
155 one header, with the values separated by a comma. */
156 header = apr_strtok(auth_hdr, ",", &header_attr);
158 while (header)
160 svn_boolean_t proto_found = FALSE;
161 auth_name = apr_strtok(header, " ", &auth_attr);
163 /* Find the matching authentication handler.
164 Note that we don't reuse the auth protocol stored in the session,
165 as that may have changed. (ex. fallback from ntlm to basic.) */
166 for (prot = serf_auth_protocols; prot->code != 0; ++prot)
168 if (code == prot->code && strcmp(auth_name, prot->auth_name) == 0)
170 svn_serf__auth_handler_func_t handler = prot->handle_func;
171 svn_error_t *err = NULL;
173 /* If this is the first time we use this protocol in this session,
174 make sure to initialize the authentication part of the session
175 first. */
176 if (code == 401 && session->auth_protocol != prot)
178 err = prot->init_conn_func(session, conn, session->pool);
179 if (err == SVN_NO_ERROR)
180 session->auth_protocol = prot;
181 else
182 session->auth_protocol = NULL;
184 else if (code == 407 && session->proxy_auth_protocol != prot)
186 err = prot->init_conn_func(session, conn, session->pool);
187 if (err == SVN_NO_ERROR)
188 session->proxy_auth_protocol = prot;
189 else
190 session->proxy_auth_protocol = NULL;
193 if (err == SVN_NO_ERROR)
195 proto_found = TRUE;
196 err = handler(session, conn, request, response,
197 header, auth_attr, session->pool);
199 if (err)
201 /* If authentication fails, just try the next available
202 scheme. */
203 svn_error_clear(err);
204 proto_found = FALSE;
207 break;
210 if (proto_found)
211 break;
213 header = apr_strtok(auth_hdr, ",", &header_attr);
216 if (prot->auth_name == NULL)
218 /* Support more authentication mechanisms. */
219 return svn_error_createf(SVN_ERR_AUTHN_FAILED, NULL,
220 "%s authentication not supported.\n"
221 "Authentication failed", auth_name);
224 return SVN_NO_ERROR;
227 static svn_error_t *
228 handle_basic_auth(svn_ra_serf__session_t *session,
229 svn_ra_serf__connection_t *conn,
230 serf_request_t *request,
231 serf_bucket_t *response,
232 char *auth_hdr,
233 char *auth_attr,
234 apr_pool_t *pool)
236 void *creds;
237 char *last, *realm_name;
238 svn_auth_cred_simple_t *simple_creds;
239 const char *tmp;
240 apr_size_t tmp_len;
241 apr_port_t port;
242 int i;
244 if (!session->realm)
246 char *attr;
248 attr = apr_strtok(auth_attr, "=", &last);
249 if (strcmp(attr, "realm") == 0)
251 realm_name = apr_strtok(NULL, "=", &last);
252 if (realm_name[0] == '\"')
254 apr_size_t realm_len;
256 realm_len = strlen(realm_name);
257 if (realm_name[realm_len - 1] == '\"')
259 realm_name[realm_len - 1] = '\0';
260 realm_name++;
264 else
266 return svn_error_create
267 (SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
268 _("Missing 'realm' attribute in Authorization header."));
270 if (!realm_name)
272 return svn_error_create
273 (SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
274 _("Missing 'realm' attribute in Authorization header."));
277 if (session->repos_url.port_str)
279 port = session->repos_url.port;
281 else
283 port = apr_uri_port_of_scheme(session->repos_url.scheme);
286 session->realm = apr_psprintf(session->pool, "<%s://%s:%d> %s",
287 session->repos_url.scheme,
288 session->repos_url.hostname,
289 port,
290 realm_name);
292 SVN_ERR(svn_auth_first_credentials(&creds,
293 &session->auth_state,
294 SVN_AUTH_CRED_SIMPLE,
295 session->realm,
296 session->wc_callbacks->auth_baton,
297 session->pool));
299 else
301 SVN_ERR(svn_auth_next_credentials(&creds,
302 session->auth_state,
303 session->pool));
306 session->auth_attempts++;
308 if (!creds || session->auth_attempts > 4)
310 /* No more credentials. */
311 return svn_error_create(SVN_ERR_AUTHN_FAILED, NULL,
312 "No more credentials or we tried too many times.\n"
313 "Authentication failed");
316 simple_creds = creds;
318 tmp = apr_pstrcat(session->pool,
319 simple_creds->username, ":", simple_creds->password, NULL);
320 tmp_len = strlen(tmp);
322 svn_ra_serf__encode_auth_header(session->auth_protocol->auth_name,
323 &session->auth_value, tmp, tmp_len, pool);
324 session->auth_header = "Authorization";
326 /* FIXME Come up with a cleaner way of changing the connection auth. */
327 for (i = 0; i < session->num_conns; i++)
329 session->conns[i]->auth_header = session->auth_header;
330 session->conns[i]->auth_value = session->auth_value;
333 return SVN_NO_ERROR;
336 static svn_error_t *
337 init_basic_connection(svn_ra_serf__session_t *session,
338 svn_ra_serf__connection_t *conn,
339 apr_pool_t *pool)
341 conn->auth_header = session->auth_header;
342 conn->auth_value = session->auth_value;
344 return SVN_NO_ERROR;
347 static svn_error_t *
348 setup_request_basic_auth(svn_ra_serf__connection_t *conn,
349 serf_bucket_t *hdrs_bkt)
351 /* Take the default authentication header for this connection, if any. */
352 if (conn->auth_header && conn->auth_value)
354 serf_bucket_headers_setn(hdrs_bkt, conn->auth_header, conn->auth_value);
357 return SVN_NO_ERROR;
360 static svn_error_t *
361 handle_proxy_basic_auth(svn_ra_serf__session_t *session,
362 svn_ra_serf__connection_t *conn,
363 serf_request_t *request,
364 serf_bucket_t *response,
365 char *auth_hdr,
366 char *auth_attr,
367 apr_pool_t *pool)
369 const char *tmp;
370 apr_size_t tmp_len;
371 int i;
373 tmp = apr_pstrcat(session->pool,
374 session->proxy_username, ":",
375 session->proxy_password, NULL);
376 tmp_len = strlen(tmp);
378 session->proxy_auth_attempts++;
380 if (session->proxy_auth_attempts > 1)
382 /* No more credentials. */
383 return svn_error_create(SVN_ERR_AUTHN_FAILED, NULL,
384 "Proxy authentication failed");
387 svn_ra_serf__encode_auth_header(session->proxy_auth_protocol->auth_name,
388 &session->proxy_auth_value,
389 tmp, tmp_len, pool);
390 session->proxy_auth_header = "Proxy-Authorization";
392 /* FIXME Come up with a cleaner way of changing the connection auth. */
393 for (i = 0; i < session->num_conns; i++)
395 session->conns[i]->proxy_auth_header = session->proxy_auth_header;
396 session->conns[i]->proxy_auth_value = session->proxy_auth_value;
399 return SVN_NO_ERROR;
402 static svn_error_t *
403 init_proxy_basic_connection(svn_ra_serf__session_t *session,
404 svn_ra_serf__connection_t *conn,
405 apr_pool_t *pool)
407 conn->proxy_auth_header = session->proxy_auth_header;
408 conn->proxy_auth_value = session->proxy_auth_value;
410 return SVN_NO_ERROR;
413 static svn_error_t *
414 setup_request_proxy_basic_auth(svn_ra_serf__connection_t *conn,
415 serf_bucket_t *hdrs_bkt)
417 /* Take the default authentication header for this connection, if any. */
418 if (conn->proxy_auth_header && conn->proxy_auth_value)
420 serf_bucket_headers_setn(hdrs_bkt, conn->proxy_auth_header,
421 conn->proxy_auth_value);
424 return SVN_NO_ERROR;