Avoid more build warnings from autogen.sh
[svn.git] / subversion / libsvn_ra_serf / win32_auth_sspi.c
blobb242493b57c6eac4d8e4064cd003173c256ca44c
1 /*
2 * win32_auth_sspi.c : authn implementation through SSPI
4 * ====================================================================
5 * Copyright (c) 2007 CollabNet. All rights reserved.
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.
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 * ====================================================================
19 /* TODO:
20 - remove NTLM dependency so we can reuse SSPI for Kerberos later. */
23 * NTLM authentication for HTTP
25 * 1. C --> S: GET
27 * C <-- S: 401 Authentication Required
28 * WWW-Authenticate: NTLM
30 * -> Initialize the NTLM authentication handler.
32 * 2. C --> S: GET
33 * Authorization: NTLM <Base64 encoded Type 1 message>
34 * sspi_ctx->state = sspi_auth_in_progress;
36 * C <-- S: 401 Authentication Required
37 * WWW-Authenticate: NTLM <Base64 encoded Type 2 message>
39 * 3. C --> S: GET
40 * Authorization: NTLM <Base64 encoded Type 3 message>
41 * sspi_ctx->state = sspi_auth_completed;
43 * C <-- S: 200 Ok
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.
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.
55 /*** Includes ***/
56 #ifdef WIN32
57 #ifdef APR_HAVE_IPV6
58 #include <winsock2.h>
59 #include <Ws2tcpip.h>
60 #include <Wspiapi.h>
61 #endif
62 #include <windows.h>
63 #endif
64 #include <string.h>
66 #include <apr_base64.h>
68 #include "svn_error.h"
70 #include "ra_serf.h"
71 #include "win32_auth_sspi.h"
73 #ifdef SVN_RA_SERF_SSPI_ENABLED
75 /*** Global variables ***/
76 HANDLE security_dll = INVALID_HANDLE_VALUE;
77 INIT_SECURITY_INTERFACE InitSecurityInterface_;
78 static PSecurityFunctionTable sspi = NULL;
79 static unsigned int ntlm_maxtokensize = 0;
81 #define SECURITY_DLL "security.dll"
83 /* Loads security.dll in memory on the first call. Afterwards the
84 function table SSPI is loaded which we can use it to call SSPI's
85 public functions. */
86 static svn_error_t *
87 load_security_dll()
89 if (security_dll != INVALID_HANDLE_VALUE)
90 return SVN_NO_ERROR;
92 security_dll = LoadLibrary(SECURITY_DLL);
93 if (security_dll != INVALID_HANDLE_VALUE)
95 /* Load the function(s) */
96 InitSecurityInterface_ =
97 (INIT_SECURITY_INTERFACE)GetProcAddress(security_dll,
98 "InitSecurityInterfaceA");
99 sspi = InitSecurityInterface_();
101 if (sspi)
102 return SVN_NO_ERROR;
105 /* Initialization failed, clean up and raise error */
106 if (security_dll)
107 FreeLibrary(security_dll);
109 return svn_error_createf
110 (SVN_ERR_RA_SERF_SSPI_INITIALISATION_FAILED, NULL,
111 "SSPI Initialization failed.");
114 /* Calculates the maximum token size based on the authentication protocol. */
115 static svn_error_t *
116 sspi_maxtokensize(char *auth_pkg, unsigned int *maxtokensize)
118 SECURITY_STATUS status;
119 SecPkgInfo *sec_pkg_info = NULL;
121 status = sspi->QuerySecurityPackageInfo(auth_pkg,
122 &sec_pkg_info);
123 if (status == SEC_E_OK)
125 *maxtokensize = sec_pkg_info->cbMaxToken;
126 sspi->FreeContextBuffer(sec_pkg_info);
128 else
129 return svn_error_createf
130 (SVN_ERR_RA_SERF_SSPI_INITIALISATION_FAILED, NULL,
131 "SSPI Initialization failed.");
133 return SVN_NO_ERROR;
136 svn_error_t *
137 init_sspi_connection(svn_ra_serf__session_t *session,
138 svn_ra_serf__connection_t *conn,
139 apr_pool_t *pool)
141 const char *tmp;
142 apr_size_t tmp_len;
144 SVN_ERR(load_security_dll());
146 conn->sspi_context = (serf_sspi_context_t*)
147 apr_palloc(pool, sizeof(serf_sspi_context_t));
148 conn->sspi_context->ctx.dwLower = 0;
149 conn->sspi_context->ctx.dwUpper = 0;
150 conn->sspi_context->state = sspi_auth_not_started;
152 /* Setup the initial request to the server with an SSPI header */
153 SVN_ERR(sspi_get_credentials(NULL, 0, &tmp, &tmp_len,
154 conn->sspi_context));
155 svn_ra_serf__encode_auth_header("NTLM", &conn->auth_value, tmp, tmp_len,
156 pool);
157 conn->auth_header = "Authorization";
159 /* Make serf send the initial requests one by one */
160 serf_connection_set_max_outstanding_requests(conn->conn, 1);
162 return SVN_NO_ERROR;
165 svn_error_t *
166 handle_sspi_auth(svn_ra_serf__session_t *session,
167 svn_ra_serf__connection_t *conn,
168 serf_request_t *request,
169 serf_bucket_t *response,
170 char *auth_hdr,
171 char *auth_attr,
172 apr_pool_t *pool)
174 const char *tmp;
175 char *base64_token, *token = NULL, *last;
176 apr_size_t tmp_len, token_len = 0;
178 base64_token = apr_strtok(auth_attr, " ", &last);
179 if (base64_token)
181 token_len = apr_base64_decode_len(base64_token);
182 token = apr_palloc(pool, token_len);
183 apr_base64_decode(token, base64_token);
186 /* We can get a whole batch of 401 responses from the server, but we should
187 only start the authentication phase once, so if we started authentication
188 ignore all responses with initial NTLM authentication header. */
189 if (!token && conn->sspi_context->state != sspi_auth_not_started)
190 return SVN_NO_ERROR;
192 SVN_ERR(sspi_get_credentials(token, token_len, &tmp, &tmp_len,
193 conn->sspi_context));
195 svn_ra_serf__encode_auth_header(session->auth_protocol->auth_name,
196 &conn->auth_value, tmp, tmp_len, pool);
197 conn->auth_header = "Authorization";
199 /* If the handshake is finished tell serf it can send as much requests as it
200 likes. */
201 if (conn->sspi_context->state == sspi_auth_completed)
202 serf_connection_set_max_outstanding_requests(conn->conn, 0);
204 return SVN_NO_ERROR;
207 svn_error_t *
208 setup_request_sspi_auth(svn_ra_serf__connection_t *conn,
209 serf_bucket_t *hdrs_bkt)
211 /* Take the default authentication header for this connection, if any. */
212 if (conn->auth_header && conn->auth_value)
214 serf_bucket_headers_setn(hdrs_bkt, conn->auth_header, conn->auth_value);
215 conn->auth_header = NULL;
216 conn->auth_value = NULL;
219 return SVN_NO_ERROR;
222 svn_error_t *
223 sspi_get_credentials(char *token, apr_size_t token_len, const char **buf,
224 apr_size_t *buf_len, serf_sspi_context_t *sspi_ctx)
226 SecBuffer in_buf, out_buf;
227 SecBufferDesc in_buf_desc, out_buf_desc;
228 SECURITY_STATUS status;
229 DWORD ctx_attr;
230 TimeStamp expires;
231 CredHandle creds;
232 char *target = NULL;
233 CtxtHandle *ctx = &(sspi_ctx->ctx);
235 if (ntlm_maxtokensize == 0)
236 sspi_maxtokensize("NTLM", &ntlm_maxtokensize);
237 /* Prepare inbound buffer. */
238 in_buf.BufferType = SECBUFFER_TOKEN;
239 in_buf.cbBuffer = token_len;
240 in_buf.pvBuffer = token;
241 in_buf_desc.cBuffers = 1;
242 in_buf_desc.ulVersion = SECBUFFER_VERSION;
243 in_buf_desc.pBuffers = &in_buf;
245 /* Prepare outbound buffer. */
246 out_buf.BufferType = SECBUFFER_TOKEN;
247 out_buf.cbBuffer = ntlm_maxtokensize;
248 out_buf.pvBuffer = (char*)malloc(ntlm_maxtokensize);
249 out_buf_desc.cBuffers = 1;
250 out_buf_desc.ulVersion = SECBUFFER_VERSION;
251 out_buf_desc.pBuffers = &out_buf;
253 /* Try to accept the server token. */
254 status = sspi->AcquireCredentialsHandle(NULL, /* current user */
255 "NTLM",
256 SECPKG_CRED_OUTBOUND,
257 NULL, NULL,
258 NULL, NULL,
259 &creds,
260 &expires);
262 if (status != SEC_E_OK)
263 return svn_error_createf
264 (SVN_ERR_RA_SERF_SSPI_INITIALISATION_FAILED, NULL,
265 "SSPI Initialization failed.");
267 status = sspi->InitializeSecurityContext(&creds,
268 ctx != NULL && ctx->dwLower != 0
269 ? ctx
270 : NULL,
271 target,
272 ISC_REQ_REPLAY_DETECT |
273 ISC_REQ_SEQUENCE_DETECT |
274 ISC_REQ_CONFIDENTIALITY |
275 ISC_REQ_DELEGATE,
277 SECURITY_NATIVE_DREP,
278 &in_buf_desc,
280 ctx,
281 &out_buf_desc,
282 &ctx_attr,
283 &expires);
285 /* Finish authentication if SSPI requires so. */
286 if (status == SEC_I_COMPLETE_NEEDED
287 || status == SEC_I_COMPLETE_AND_CONTINUE)
289 if (sspi->CompleteAuthToken != NULL)
290 sspi->CompleteAuthToken(ctx, &out_buf_desc);
293 *buf = out_buf.pvBuffer;
294 *buf_len = out_buf.cbBuffer;
296 switch (status)
298 case SEC_E_OK:
299 case SEC_I_COMPLETE_NEEDED:
300 sspi_ctx->state = sspi_auth_completed;
301 break;
303 case SEC_I_CONTINUE_NEEDED:
304 case SEC_I_COMPLETE_AND_CONTINUE:
305 sspi_ctx->state = sspi_auth_in_progress;
306 break;
308 default:
309 return svn_error_createf(SVN_ERR_AUTHN_FAILED, NULL,
310 "Authentication failed with error 0x%x.", status);
313 return SVN_NO_ERROR;
316 #endif /* SVN_RA_SERF_SSPI_ENABLED */