~ubuntu-branches/ubuntu/trusty/subversion/trusty-proposed

« back to all changes in this revision

Viewing changes to subversion/libsvn_ra_serf/win32_auth_sspi.c

  • Committer: Package Import Robot
  • Author(s): Andy Whitcroft
  • Date: 2012-06-21 15:36:36 UTC
  • mfrom: (0.4.13 sid)
  • Revision ID: package-import@ubuntu.com-20120621153636-amqqmuidgwgxz1ly
Tags: 1.7.5-1ubuntu1
* Merge from Debian unstable.  Remaining changes:
  - Create pot file on build.
  - Build a python-subversion-dbg package.
  - Build-depend on python-dbg.
  - Build-depend on default-jre-headless/-jdk.
  - Do not apply java-build patch.
  - debian/rules: Manually create the doxygen output directory, otherwise
    we get weird build failures when running parallel builds.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * win32_auth_sspi.c : authn implementation through SSPI
3
 
 *
4
 
 * ====================================================================
5
 
 * Copyright (c) 2007 CollabNet.  All rights reserved.
6
 
 *
7
 
 * This software is licensed as described in the file COPYING, which
8
 
 * you should have received as part of this distribution.  The terms
9
 
 * are also available at http://subversion.tigris.org/license-1.html.
10
 
 * If newer versions of this license are posted there, you may use a
11
 
 * newer version instead, at your option.
12
 
 *
13
 
 * This software consists of voluntary contributions made by many
14
 
 * individuals.  For exact contribution history, see the revision
15
 
 * history and logs, available at http://subversion.tigris.org/.
16
 
 * ====================================================================
17
 
 */
18
 
 
19
 
/* TODO:
20
 
   - remove NTLM dependency so we can reuse SSPI for Kerberos later. */
21
 
 
22
 
/*
23
 
 * NTLM authentication for HTTP
24
 
 *
25
 
 * 1. C  --> S:    GET
26
 
 *
27
 
 *    C <--  S:    401 Authentication Required
28
 
 *                 WWW-Authenticate: NTLM
29
 
 *
30
 
 * -> Initialize the NTLM authentication handler.
31
 
 *
32
 
 * 2. C  --> S:    GET
33
 
 *                 Authorization: NTLM <Base64 encoded Type 1 message>
34
 
 *                 sspi_ctx->state = sspi_auth_in_progress;
35
 
 *
36
 
 *    C <--  S:    401 Authentication Required
37
 
 *                 WWW-Authenticate: NTLM <Base64 encoded Type 2 message>
38
 
 *
39
 
 * 3. C  --> S:    GET
40
 
 *                 Authorization: NTLM <Base64 encoded Type 3 message>
41
 
 *                 sspi_ctx->state = sspi_auth_completed;
42
 
 *
43
 
 *    C <--  S:    200 Ok
44
 
 *
45
 
 * This handshake is required for every new connection. If the handshake is
46
 
 * completed successfully, all other requested on the same connection will
47
 
 * be authenticated without needing to pass the WWW-Authenticate header.
48
 
 *
49
 
 * Note: Step 1 of the handshake will only happen on the first connection, once
50
 
 * we know the server requires NTLM authentication, the initial requests on the
51
 
 * other connections will include the NTLM Type 1 message, so we start at
52
 
 * step 2 in the handshake.
53
 
 */
54
 
 
55
 
/*** Includes ***/
56
 
#include <string.h>
57
 
 
58
 
#include <apr.h>
59
 
#include <apr_base64.h>
60
 
 
61
 
#include "svn_error.h"
62
 
 
63
 
#include "ra_serf.h"
64
 
#include "win32_auth_sspi.h"
65
 
 
66
 
#include "private/svn_atomic.h"
67
 
 
68
 
#ifdef SVN_RA_SERF_SSPI_ENABLED
69
 
 
70
 
/*** Global variables ***/
71
 
static svn_atomic_t sspi_initialized = 0;
72
 
static PSecurityFunctionTable sspi = NULL;
73
 
static unsigned int ntlm_maxtokensize = 0;
74
 
 
75
 
/* Loads the SSPI function table we can use to call SSPI's public functions.
76
 
 * Accepted by svn_atomic__init_once()
77
 
 */
78
 
static svn_error_t *
79
 
initialize_sspi(void *baton, apr_pool_t* pool)
80
 
{
81
 
  sspi = InitSecurityInterface();
82
 
 
83
 
  if (sspi)
84
 
    return SVN_NO_ERROR;
85
 
 
86
 
  return svn_error_createf
87
 
          (SVN_ERR_RA_SERF_SSPI_INITIALISATION_FAILED, NULL,
88
 
           "SSPI Initialization failed.");
89
 
}
90
 
 
91
 
/* Calculates the maximum token size based on the authentication protocol. */
92
 
static svn_error_t *
93
 
sspi_maxtokensize(char *auth_pkg, unsigned int *maxtokensize)
94
 
{
95
 
  SECURITY_STATUS status;
96
 
  SecPkgInfo *sec_pkg_info = NULL;
97
 
 
98
 
  status = sspi->QuerySecurityPackageInfo(auth_pkg,
99
 
                                          &sec_pkg_info);
100
 
  if (status == SEC_E_OK)
101
 
    {
102
 
      *maxtokensize = sec_pkg_info->cbMaxToken;
103
 
      sspi->FreeContextBuffer(sec_pkg_info);
104
 
    }
105
 
  else
106
 
    return svn_error_createf
107
 
      (SVN_ERR_RA_SERF_SSPI_INITIALISATION_FAILED, NULL,
108
 
       "SSPI Initialization failed.");
109
 
 
110
 
  return SVN_NO_ERROR;
111
 
}
112
 
 
113
 
svn_error_t *
114
 
init_sspi_connection(svn_ra_serf__session_t *session,
115
 
                     svn_ra_serf__connection_t *conn,
116
 
                     apr_pool_t *pool)
117
 
{
118
 
  const char *tmp;
119
 
  apr_size_t tmp_len;
120
 
 
121
 
  SVN_ERR(svn_atomic__init_once(&sspi_initialized,
122
 
                                initialize_sspi, NULL, pool));
123
 
 
124
 
  conn->sspi_context = (serf_sspi_context_t*)
125
 
    apr_palloc(pool, sizeof(serf_sspi_context_t));
126
 
  conn->sspi_context->ctx.dwLower = 0;
127
 
  conn->sspi_context->ctx.dwUpper = 0;
128
 
  conn->sspi_context->state = sspi_auth_not_started;
129
 
 
130
 
  /* Setup the initial request to the server with an SSPI header */
131
 
  SVN_ERR(sspi_get_credentials(NULL, 0, &tmp, &tmp_len,
132
 
                               conn->sspi_context));
133
 
  svn_ra_serf__encode_auth_header("NTLM", &conn->auth_value, tmp, tmp_len,
134
 
                                  pool);
135
 
  conn->auth_header = "Authorization";
136
 
 
137
 
  /* Make serf send the initial requests one by one */
138
 
  serf_connection_set_max_outstanding_requests(conn->conn, 1);
139
 
 
140
 
  return SVN_NO_ERROR;
141
 
}
142
 
 
143
 
svn_error_t *
144
 
handle_sspi_auth(svn_ra_serf__session_t *session,
145
 
                 svn_ra_serf__connection_t *conn,
146
 
                 serf_request_t *request,
147
 
                 serf_bucket_t *response,
148
 
                 char *auth_hdr,
149
 
                 char *auth_attr,
150
 
                 apr_pool_t *pool)
151
 
{
152
 
  const char *tmp;
153
 
  char *base64_token, *token = NULL, *last;
154
 
  apr_size_t tmp_len, token_len = 0;
155
 
 
156
 
  base64_token = apr_strtok(auth_attr, " ", &last);
157
 
  if (base64_token)
158
 
    {
159
 
      token_len = apr_base64_decode_len(base64_token);
160
 
      token = apr_palloc(pool, token_len);
161
 
      apr_base64_decode(token, base64_token);
162
 
    }
163
 
 
164
 
  /* We can get a whole batch of 401 responses from the server, but we should
165
 
     only start the authentication phase once, so if we started authentication
166
 
     ignore all responses with initial NTLM authentication header. */
167
 
  if (!token && conn->sspi_context->state != sspi_auth_not_started)
168
 
    return SVN_NO_ERROR;
169
 
 
170
 
  SVN_ERR(sspi_get_credentials(token, token_len, &tmp, &tmp_len,
171
 
                               conn->sspi_context));
172
 
 
173
 
  svn_ra_serf__encode_auth_header(session->auth_protocol->auth_name,
174
 
                                  &conn->auth_value, tmp, tmp_len, pool);
175
 
  conn->auth_header = "Authorization";
176
 
 
177
 
  /* If the handshake is finished tell serf it can send as much requests as it
178
 
     likes. */
179
 
  if (conn->sspi_context->state == sspi_auth_completed)
180
 
    serf_connection_set_max_outstanding_requests(conn->conn, 0);
181
 
 
182
 
  return SVN_NO_ERROR;
183
 
}
184
 
 
185
 
svn_error_t *
186
 
setup_request_sspi_auth(svn_ra_serf__connection_t *conn,
187
 
                        serf_bucket_t *hdrs_bkt)
188
 
{
189
 
  /* Take the default authentication header for this connection, if any. */
190
 
  if (conn->auth_header && conn->auth_value)
191
 
    {
192
 
      serf_bucket_headers_setn(hdrs_bkt, conn->auth_header, conn->auth_value);
193
 
      conn->auth_header = NULL;
194
 
      conn->auth_value = NULL;
195
 
    }
196
 
 
197
 
  return SVN_NO_ERROR;
198
 
}
199
 
 
200
 
svn_error_t *
201
 
sspi_get_credentials(char *token, apr_size_t token_len, const char **buf,
202
 
                     apr_size_t *buf_len, serf_sspi_context_t *sspi_ctx)
203
 
{
204
 
  SecBuffer in_buf, out_buf;
205
 
  SecBufferDesc in_buf_desc, out_buf_desc;
206
 
  SECURITY_STATUS status;
207
 
  DWORD ctx_attr;
208
 
  TimeStamp expires;
209
 
  CredHandle creds;
210
 
  char *target = NULL;
211
 
  CtxtHandle *ctx = &(sspi_ctx->ctx);
212
 
 
213
 
  if (ntlm_maxtokensize == 0)
214
 
    sspi_maxtokensize("NTLM", &ntlm_maxtokensize);
215
 
  /* Prepare inbound buffer. */
216
 
  in_buf.BufferType = SECBUFFER_TOKEN;
217
 
  in_buf.cbBuffer   = token_len;
218
 
  in_buf.pvBuffer   = token;
219
 
  in_buf_desc.cBuffers  = 1;
220
 
  in_buf_desc.ulVersion = SECBUFFER_VERSION;
221
 
  in_buf_desc.pBuffers  = &in_buf;
222
 
 
223
 
  /* Prepare outbound buffer. */
224
 
  out_buf.BufferType = SECBUFFER_TOKEN;
225
 
  out_buf.cbBuffer   = ntlm_maxtokensize;
226
 
  out_buf.pvBuffer   = (char*)malloc(ntlm_maxtokensize);
227
 
  out_buf_desc.cBuffers  = 1;
228
 
  out_buf_desc.ulVersion = SECBUFFER_VERSION;
229
 
  out_buf_desc.pBuffers  = &out_buf;
230
 
 
231
 
  /* Try to accept the server token. */
232
 
  status = sspi->AcquireCredentialsHandle(NULL, /* current user */
233
 
                                          "NTLM",
234
 
                                          SECPKG_CRED_OUTBOUND,
235
 
                                          NULL, NULL,
236
 
                                          NULL, NULL,
237
 
                                          &creds,
238
 
                                          &expires);
239
 
 
240
 
  if (status != SEC_E_OK)
241
 
    return svn_error_createf
242
 
            (SVN_ERR_RA_SERF_SSPI_INITIALISATION_FAILED, NULL,
243
 
             "SSPI Initialization failed.");
244
 
 
245
 
  status = sspi->InitializeSecurityContext(&creds,
246
 
                                           ctx != NULL && ctx->dwLower != 0
247
 
                                             ? ctx
248
 
                                             : NULL,
249
 
                                           target,
250
 
                                           ISC_REQ_REPLAY_DETECT |
251
 
                                           ISC_REQ_SEQUENCE_DETECT |
252
 
                                           ISC_REQ_CONFIDENTIALITY |
253
 
                                           ISC_REQ_DELEGATE,
254
 
                                           0,
255
 
                                           SECURITY_NATIVE_DREP,
256
 
                                           &in_buf_desc,
257
 
                                           0,
258
 
                                           ctx,
259
 
                                           &out_buf_desc,
260
 
                                           &ctx_attr,
261
 
                                           &expires);
262
 
 
263
 
  /* Finish authentication if SSPI requires so. */
264
 
  if (status == SEC_I_COMPLETE_NEEDED
265
 
      || status == SEC_I_COMPLETE_AND_CONTINUE)
266
 
    {
267
 
      if (sspi->CompleteAuthToken != NULL)
268
 
        sspi->CompleteAuthToken(ctx, &out_buf_desc);
269
 
    }
270
 
 
271
 
  *buf = out_buf.pvBuffer;
272
 
  *buf_len = out_buf.cbBuffer;
273
 
 
274
 
  switch (status)
275
 
    {
276
 
      case SEC_E_OK:
277
 
      case SEC_I_COMPLETE_NEEDED:
278
 
          sspi_ctx->state = sspi_auth_completed;
279
 
          break;
280
 
 
281
 
      case SEC_I_CONTINUE_NEEDED:
282
 
      case SEC_I_COMPLETE_AND_CONTINUE:
283
 
          sspi_ctx->state = sspi_auth_in_progress;
284
 
          break;
285
 
 
286
 
      default:
287
 
          return svn_error_createf(SVN_ERR_AUTHN_FAILED, NULL,
288
 
                "Authentication failed with error 0x%x.", status);
289
 
    }
290
 
 
291
 
  return SVN_NO_ERROR;
292
 
}
293
 
 
294
 
/* Proxy authentication */
295
 
 
296
 
svn_error_t *
297
 
init_proxy_sspi_connection(svn_ra_serf__session_t *session,
298
 
                           svn_ra_serf__connection_t *conn,
299
 
                           apr_pool_t *pool)
300
 
{
301
 
  const char *tmp;
302
 
  apr_size_t tmp_len;
303
 
 
304
 
  SVN_ERR(svn_atomic__init_once(&sspi_initialized,
305
 
                                initialize_sspi, NULL, pool));
306
 
 
307
 
  conn->proxy_sspi_context = (serf_sspi_context_t*)
308
 
    apr_palloc(pool, sizeof(serf_sspi_context_t));
309
 
  conn->proxy_sspi_context->ctx.dwLower = 0;
310
 
  conn->proxy_sspi_context->ctx.dwUpper = 0;
311
 
  conn->proxy_sspi_context->state = sspi_auth_not_started;
312
 
 
313
 
  /* Setup the initial request to the server with an SSPI header */
314
 
  SVN_ERR(sspi_get_credentials(NULL, 0, &tmp, &tmp_len,
315
 
                               conn->proxy_sspi_context));
316
 
  svn_ra_serf__encode_auth_header("NTLM", &conn->proxy_auth_value, tmp,
317
 
                                  tmp_len,
318
 
                                  pool);
319
 
  conn->proxy_auth_header = "Proxy-Authorization";
320
 
 
321
 
  /* Make serf send the initial requests one by one */
322
 
  serf_connection_set_max_outstanding_requests(conn->conn, 1);
323
 
 
324
 
  return SVN_NO_ERROR;
325
 
}
326
 
 
327
 
svn_error_t *
328
 
handle_proxy_sspi_auth(svn_ra_serf__session_t *session,
329
 
                       svn_ra_serf__connection_t *conn,
330
 
                       serf_request_t *request,
331
 
                       serf_bucket_t *response,
332
 
                       char *auth_hdr,
333
 
                       char *auth_attr,
334
 
                       apr_pool_t *pool)
335
 
{
336
 
  const char *tmp;
337
 
  char *base64_token, *token = NULL, *last;
338
 
  apr_size_t tmp_len, token_len = 0;
339
 
 
340
 
  base64_token = apr_strtok(auth_attr, " ", &last);
341
 
  if (base64_token)
342
 
    {
343
 
      token_len = apr_base64_decode_len(base64_token);
344
 
      token = apr_palloc(pool, token_len);
345
 
      apr_base64_decode(token, base64_token);
346
 
    }
347
 
 
348
 
  /* We can get a whole batch of 401 responses from the server, but we should
349
 
     only start the authentication phase once, so if we started authentication
350
 
     ignore all responses with initial NTLM authentication header. */
351
 
  if (!token && conn->proxy_sspi_context->state != sspi_auth_not_started)
352
 
    return SVN_NO_ERROR;
353
 
 
354
 
  SVN_ERR(sspi_get_credentials(token, token_len, &tmp, &tmp_len,
355
 
                               conn->proxy_sspi_context));
356
 
 
357
 
  svn_ra_serf__encode_auth_header(session->proxy_auth_protocol->auth_name,
358
 
                                  &conn->proxy_auth_value, tmp, tmp_len, pool);
359
 
  conn->proxy_auth_header = "Proxy-Authorization";
360
 
 
361
 
  /* If the handshake is finished tell serf it can send as much requests as it
362
 
     likes. */
363
 
  if (conn->proxy_sspi_context->state == sspi_auth_completed)
364
 
    serf_connection_set_max_outstanding_requests(conn->conn, 0);
365
 
 
366
 
  return SVN_NO_ERROR;
367
 
}
368
 
 
369
 
svn_error_t *
370
 
setup_request_proxy_sspi_auth(svn_ra_serf__connection_t *conn,
371
 
                              serf_bucket_t *hdrs_bkt)
372
 
{
373
 
  /* Take the default authentication header for this connection, if any. */
374
 
  if (conn->proxy_auth_header && conn->proxy_auth_value)
375
 
    {
376
 
      serf_bucket_headers_setn(hdrs_bkt, conn->proxy_auth_header,
377
 
                               conn->proxy_auth_value);
378
 
      conn->proxy_auth_header = NULL;
379
 
      conn->proxy_auth_value = NULL;
380
 
    }
381
 
 
382
 
  return SVN_NO_ERROR;
383
 
}
384
 
 
385
 
#endif /* SVN_RA_SERF_SSPI_ENABLED */