purple: work around broken dbus-server.h
[siplcs.git] / src / core / sip-sec-sspi.c
blobb4c184af0fa870326fc38c51f35ff6aafb9acb91
1 /**
2 * @file sip-sec-sspi.c
4 * pidgin-sipe
6 * Copyright (C) 2011-2015 SIPE Project <http://sipe.sourceforge.net/>
7 * Copyright (C) 2009 pier11 <pier11@operamail.com>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 #ifndef _WIN32
26 #error sip-sec-sspi.c can only be compiled for Windows builds
27 #endif
29 #include <windows.h>
30 #include <rpc.h>
31 #ifndef SECURITY_WIN32
32 #define SECURITY_WIN32 1
33 #endif
34 #include <security.h>
36 #include <string.h>
38 #include <glib.h>
40 #include "sipe-common.h"
41 #include "sip-sec.h"
42 #include "sip-sec-mech.h"
43 #include "sip-sec-sspi.h"
44 #include "sipe-backend.h"
45 #include "sipe-core.h"
46 #include "sipe-utils.h"
48 /* Mechanism names */
49 static const gchar * const mech_names[] = {
50 "", /* SIPE_AUTHENTICATION_TYPE_UNSET */
51 "", /* SIPE_AUTHENTICATION_TYPE_BASIC */
52 "NTLM", /* SIPE_AUTHENTICATION_TYPE_NTLM */
53 "Kerberos", /* SIPE_AUTHENTICATION_TYPE_KERBEROS */
54 "Negotiate", /* SIPE_AUTHENTICATION_TYPE_NEGOTIATE */
55 "", /* SIPE_AUTHENTICATION_TYPE_TLS_DSK */
56 "", /* SIPE_AUTHENTICATION_TYPE_AUTOMATIC */
59 #ifndef ISC_REQ_IDENTIFY
60 #define ISC_REQ_IDENTIFY 0x00002000
61 #endif
63 typedef struct _context_sspi {
64 struct sip_sec_context common;
65 CredHandle* cred_sspi;
66 CtxtHandle* ctx_sspi;
67 } *context_sspi;
69 #define SIP_SEC_FLAG_SSPI_SIP_NTLM 0x00010000
71 /* Utility Functions */
73 static void
74 sip_sec_sspi_print_error(const gchar *func,
75 SECURITY_STATUS ret)
77 gchar *error_message;
78 static char *buff;
79 guint buff_length;
81 buff_length = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
82 FORMAT_MESSAGE_ALLOCATE_BUFFER |
83 FORMAT_MESSAGE_IGNORE_INSERTS,
85 ret,
87 (LPTSTR)&buff,
88 16384,
89 0);
90 error_message = g_strndup(buff, buff_length);
91 LocalFree(buff);
93 SIPE_DEBUG_ERROR("SSPI ERROR [%d] in %s: %s", (int)ret, func, error_message);
94 g_free(error_message);
97 /* Returns interval in seconds from now till provided value */
98 static guint
99 sip_sec_get_interval_from_now_sec(TimeStamp timestamp)
101 SYSTEMTIME stNow;
102 FILETIME ftNow;
103 ULARGE_INTEGER uliNow, uliTo;
105 GetLocalTime(&stNow);
106 SystemTimeToFileTime(&stNow, &ftNow);
108 uliNow.LowPart = ftNow.dwLowDateTime;
109 uliNow.HighPart = ftNow.dwHighDateTime;
111 uliTo.LowPart = timestamp.LowPart;
112 uliTo.HighPart = timestamp.HighPart;
114 return((uliTo.QuadPart - uliNow.QuadPart)/10/1000/1000);
117 static void
118 sip_sec_destroy_sspi_context(context_sspi context)
120 if (context->ctx_sspi) {
121 DeleteSecurityContext(context->ctx_sspi);
122 g_free(context->ctx_sspi);
123 context->ctx_sspi = NULL;
125 if (context->cred_sspi) {
126 FreeCredentialsHandle(context->cred_sspi);
127 g_free(context->cred_sspi);
128 context->cred_sspi = NULL;
132 /* sip-sec-mech.h API implementation for SSPI - Kerberos, NTLM and Negotiate */
134 static gboolean
135 sip_sec_acquire_cred__sspi(SipSecContext context,
136 const gchar *username,
137 const gchar *password)
139 SECURITY_STATUS ret;
140 TimeStamp expiry;
141 SEC_WINNT_AUTH_IDENTITY auth_identity;
142 context_sspi ctx = (context_sspi)context;
143 gchar *domain_tmp = NULL;
144 gchar *user_tmp = NULL;
146 /* this is the first time we are allowed to set private flags */
147 if (((context->flags & SIP_SEC_FLAG_COMMON_HTTP) == 0) &&
148 (context->type == SIPE_AUTHENTICATION_TYPE_NTLM))
149 context->flags |= SIP_SEC_FLAG_SSPI_SIP_NTLM;
151 if ((context->flags & SIP_SEC_FLAG_COMMON_SSO) == 0) {
152 if (is_empty(username) || is_empty(password)) {
153 SIPE_DEBUG_ERROR_NOFORMAT("sip_sec_acquire_cred__sspi: no valid authentication information provided");
154 return FALSE;
157 memset(&auth_identity, 0, sizeof(auth_identity));
158 auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
160 if (SIP_SEC_USERNAME_IS_ENTERPRISE) {
161 /* use username as-is, just replace enterprise marker with @ */
162 user_tmp = sipe_utils_str_replace(username,
163 SIP_SEC_USERNAME_ENTERPRISE_STRING,
164 "@");
165 } else {
166 SIP_SEC_USERNAME_SPLIT_START;
167 if (SIP_SEC_USERNAME_HAS_DOMAIN) {
168 domain_tmp = g_strdup(SIP_SEC_USERNAME_DOMAIN);
169 user_tmp = g_strdup(SIP_SEC_USERNAME_ACCOUNT);
170 auth_identity.Domain = (unsigned char *)domain_tmp;
171 auth_identity.DomainLength = strlen(domain_tmp);
173 SIP_SEC_USERNAME_SPLIT_END;
176 auth_identity.User = (unsigned char *)(user_tmp ? user_tmp : username);
177 auth_identity.UserLength = strlen((char *) auth_identity.User);
179 auth_identity.Password = (unsigned char *)password;
180 auth_identity.PasswordLength = strlen(password);
183 ctx->cred_sspi = g_malloc0(sizeof(CredHandle));
185 ret = AcquireCredentialsHandleA(NULL,
186 (SEC_CHAR *)mech_names[context->type],
187 SECPKG_CRED_OUTBOUND,
188 NULL,
189 (context->flags & SIP_SEC_FLAG_COMMON_SSO) ? NULL : &auth_identity,
190 NULL,
191 NULL,
192 ctx->cred_sspi,
193 &expiry);
195 g_free(user_tmp);
196 g_free(domain_tmp);
198 if (ret != SEC_E_OK) {
199 sip_sec_sspi_print_error("sip_sec_acquire_cred__sspi: AcquireCredentialsHandleA", ret);
200 g_free(ctx->cred_sspi);
201 ctx->cred_sspi = NULL;
202 return FALSE;
203 } else {
204 return TRUE;
208 static gboolean
209 sip_sec_init_sec_context__sspi(SipSecContext context,
210 SipSecBuffer in_buff,
211 SipSecBuffer *out_buff,
212 const gchar *service_name)
214 TimeStamp expiry;
215 SecBufferDesc input_desc, output_desc;
216 SecBuffer in_token, out_token;
217 SECURITY_STATUS ret;
218 ULONG req_flags;
219 ULONG ret_flags;
220 context_sspi ctx = (context_sspi)context;
221 CtxtHandle* out_context;
223 SIPE_DEBUG_INFO_NOFORMAT("sip_sec_init_sec_context__sspi: in use");
226 * If authentication was already completed, then this mean a new
227 * authentication handshake has started on the existing connection.
228 * We must throw away the old context, because we need a new one.
230 if ((context->flags & SIP_SEC_FLAG_COMMON_READY) &&
231 ctx->ctx_sspi) {
232 SIPE_DEBUG_INFO_NOFORMAT("sip_sec_init_sec_context__sspi: dropping old context");
233 DeleteSecurityContext(ctx->ctx_sspi);
234 g_free(ctx->ctx_sspi);
235 ctx->ctx_sspi = NULL;
236 context->flags &= ~SIP_SEC_FLAG_COMMON_READY;
239 /* reuse existing context on following calls */
240 out_context = ctx->ctx_sspi ? ctx->ctx_sspi : g_malloc0(sizeof(CtxtHandle));
242 input_desc.cBuffers = 1;
243 input_desc.pBuffers = &in_token;
244 input_desc.ulVersion = SECBUFFER_VERSION;
246 /* input token */
247 in_token.BufferType = SECBUFFER_TOKEN;
248 in_token.cbBuffer = in_buff.length;
249 in_token.pvBuffer = in_buff.value;
251 output_desc.cBuffers = 1;
252 output_desc.pBuffers = &out_token;
253 output_desc.ulVersion = SECBUFFER_VERSION;
255 /* to hold output token */
256 out_token.BufferType = SECBUFFER_TOKEN;
257 out_token.cbBuffer = 0;
258 out_token.pvBuffer = NULL;
260 req_flags = (ISC_REQ_ALLOCATE_MEMORY |
261 ISC_REQ_INTEGRITY |
262 ISC_REQ_IDENTIFY);
264 if (context->flags & SIP_SEC_FLAG_SSPI_SIP_NTLM) {
265 req_flags |= (ISC_REQ_DATAGRAM);
268 ret = InitializeSecurityContextA(ctx->cred_sspi,
269 ctx->ctx_sspi,
270 (SEC_CHAR *)service_name,
271 req_flags,
273 SECURITY_NATIVE_DREP,
274 &input_desc,
276 out_context,
277 &output_desc,
278 &ret_flags,
279 &expiry);
281 if (ret != SEC_E_OK && ret != SEC_I_CONTINUE_NEEDED) {
282 if (!ctx->ctx_sspi)
283 g_free(out_context);
284 sip_sec_destroy_sspi_context(ctx);
285 sip_sec_sspi_print_error("sip_sec_init_sec_context__sspi: InitializeSecurityContextA", ret);
286 return FALSE;
289 out_buff->length = out_token.cbBuffer;
290 if (out_token.cbBuffer) {
291 out_buff->value = g_malloc(out_token.cbBuffer);
292 memcpy(out_buff->value, out_token.pvBuffer, out_token.cbBuffer);
293 } else {
294 /* Special case: empty token */
295 out_buff->value = (guint8 *) g_strdup("");
297 FreeContextBuffer(out_token.pvBuffer);
299 ctx->ctx_sspi = out_context;
301 if (context->type == SIPE_AUTHENTICATION_TYPE_KERBEROS) {
302 context->expires = sip_sec_get_interval_from_now_sec(expiry);
305 if (ret != SEC_I_CONTINUE_NEEDED) {
306 /* Authentication is completed */
307 context->flags |= SIP_SEC_FLAG_COMMON_READY;
310 return TRUE;
313 static void
314 sip_sec_destroy_sec_context__sspi(SipSecContext context)
316 sip_sec_destroy_sspi_context((context_sspi)context);
317 g_free(context);
321 * @param message a NULL terminated string to sign
324 static gboolean
325 sip_sec_make_signature__sspi(SipSecContext context,
326 const gchar *message,
327 SipSecBuffer *signature)
329 SecBufferDesc buffs_desc;
330 SecBuffer buffs[2];
331 SECURITY_STATUS ret;
332 SecPkgContext_Sizes context_sizes;
333 guchar *signature_buff;
334 size_t signature_buff_length;
335 context_sspi ctx = (context_sspi) context;
337 ret = QueryContextAttributes(ctx->ctx_sspi,
338 SECPKG_ATTR_SIZES,
339 &context_sizes);
341 if (ret != SEC_E_OK) {
342 sip_sec_sspi_print_error("sip_sec_make_signature__sspi: QueryContextAttributes", ret);
343 return FALSE;
346 signature_buff_length = context_sizes.cbMaxSignature;
347 signature_buff = g_malloc(signature_buff_length);
349 buffs_desc.cBuffers = 2;
350 buffs_desc.pBuffers = buffs;
351 buffs_desc.ulVersion = SECBUFFER_VERSION;
353 /* message to sign */
354 buffs[0].BufferType = SECBUFFER_DATA;
355 buffs[0].cbBuffer = strlen(message);
356 buffs[0].pvBuffer = (PVOID)message;
358 /* to hold signature */
359 buffs[1].BufferType = SECBUFFER_TOKEN;
360 buffs[1].cbBuffer = signature_buff_length;
361 buffs[1].pvBuffer = signature_buff;
363 ret = MakeSignature(ctx->ctx_sspi,
364 (ULONG)0,
365 &buffs_desc,
366 100);
367 if (ret != SEC_E_OK) {
368 sip_sec_sspi_print_error("sip_sec_make_signature__sspi: MakeSignature", ret);
369 g_free(signature_buff);
370 return FALSE;
373 signature->value = signature_buff;
374 signature->length = buffs[1].cbBuffer;
376 return TRUE;
380 * @param message a NULL terminated string to check signature of
381 * @return TRUE on success
383 static gboolean
384 sip_sec_verify_signature__sspi(SipSecContext context,
385 const gchar *message,
386 SipSecBuffer signature)
388 SecBufferDesc buffs_desc;
389 SecBuffer buffs[2];
390 SECURITY_STATUS ret;
392 buffs_desc.cBuffers = 2;
393 buffs_desc.pBuffers = buffs;
394 buffs_desc.ulVersion = SECBUFFER_VERSION;
396 /* message to sign */
397 buffs[0].BufferType = SECBUFFER_DATA;
398 buffs[0].cbBuffer = strlen(message);
399 buffs[0].pvBuffer = (PVOID)message;
401 /* signature to check */
402 buffs[1].BufferType = SECBUFFER_TOKEN;
403 buffs[1].cbBuffer = signature.length;
404 buffs[1].pvBuffer = signature.value;
406 ret = VerifySignature(((context_sspi)context)->ctx_sspi,
407 &buffs_desc,
411 if (ret != SEC_E_OK) {
412 sip_sec_sspi_print_error("sip_sec_verify_signature__sspi: VerifySignature", ret);
413 return FALSE;
416 return TRUE;
419 /* SSPI implements SPNEGO (RFC 4559) */
420 static const gchar *
421 sip_sec_context_name__sspi(SipSecContext context)
423 return(mech_names[context->type]);
426 SipSecContext
427 sip_sec_create_context__sspi(SIPE_UNUSED_PARAMETER guint type)
429 context_sspi context = g_malloc0(sizeof(struct _context_sspi));
430 if (!context) return(NULL);
432 context->common.acquire_cred_func = sip_sec_acquire_cred__sspi;
433 context->common.init_context_func = sip_sec_init_sec_context__sspi;
434 context->common.destroy_context_func = sip_sec_destroy_sec_context__sspi;
435 context->common.make_signature_func = sip_sec_make_signature__sspi;
436 context->common.verify_signature_func = sip_sec_verify_signature__sspi;
437 context->common.context_name_func = sip_sec_context_name__sspi;
439 return((SipSecContext) context);
442 gboolean sip_sec_password__sspi(void)
444 /* SSPI supports Single-Sign On */
445 return(FALSE);
449 Local Variables:
450 mode: c
451 c-file-style: "bsd"
452 indent-tabs-mode: t
453 tab-width: 8
454 End: