i18n: update bug tracker URL in script
[siplcs.git] / src / core / sip-sec-sspi.c
blobb91e3344471dc28e4b5b1d64539e7202712acef5
1 /**
2 * @file sip-sec-sspi.c
4 * pidgin-sipe
6 * Copyright (C) 2011-2013 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 "sip-sec.h"
41 #include "sip-sec-mech.h"
42 #include "sip-sec-sspi.h"
43 #include "sipe-backend.h"
44 #include "sipe-core.h"
45 #include "sipe-utils.h"
47 /* Mechanism names */
48 static const gchar * const mech_names[] = {
49 "", /* SIPE_AUTHENTICATION_TYPE_UNSET */
50 "NTLM", /* SIPE_AUTHENTICATION_TYPE_NTLM */
51 "Kerberos", /* SIPE_AUTHENTICATION_TYPE_KERBEROS */
52 "Negotiate", /* SIPE_AUTHENTICATION_TYPE_NEGOTIATE */
53 "", /* SIPE_AUTHENTICATION_TYPE_TLS_DSK */
56 #ifndef ISC_REQ_IDENTIFY
57 #define ISC_REQ_IDENTIFY 0x00002000
58 #endif
60 typedef struct _context_sspi {
61 struct sip_sec_context common;
62 CredHandle* cred_sspi;
63 CtxtHandle* ctx_sspi;
64 guint type;
65 } *context_sspi;
67 #define SIP_SEC_FLAG_SSPI_INITIAL 0x00010000
68 #define SIP_SEC_FLAG_SSPI_SIP_NTLM 0x00020000
70 static int
71 sip_sec_get_interval_from_now_sec(TimeStamp timestamp);
73 static void
74 sip_sec_sspi_print_error(const gchar *func,
75 SECURITY_STATUS ret);
77 /** internal method */
78 static void
79 sip_sec_destroy_sspi_context(context_sspi context)
81 if (context->ctx_sspi) {
82 DeleteSecurityContext(context->ctx_sspi);
83 g_free(context->ctx_sspi);
84 context->ctx_sspi = NULL;
86 if (context->cred_sspi) {
87 FreeCredentialsHandle(context->cred_sspi);
88 g_free(context->cred_sspi);
89 context->cred_sspi = NULL;
93 /* sip-sec-mech.h API implementation for SSPI - Kerberos and NTLM */
95 static gboolean
96 sip_sec_acquire_cred__sspi(SipSecContext context,
97 const gchar *domain,
98 const gchar *username,
99 const gchar *password)
101 SECURITY_STATUS ret;
102 TimeStamp expiry;
103 SEC_WINNT_AUTH_IDENTITY auth_identity;
104 context_sspi ctx = (context_sspi)context;
106 /* this is the first time we are allowed to set private flags */
107 context->flags |= SIP_SEC_FLAG_SSPI_INITIAL;
109 if (((context->flags & SIP_SEC_FLAG_COMMON_HTTP) == 0) &&
110 (ctx->type == SIPE_AUTHENTICATION_TYPE_NTLM))
111 context->flags |= SIP_SEC_FLAG_SSPI_SIP_NTLM;
113 if ((context->flags & SIP_SEC_FLAG_COMMON_SSO) == 0) {
114 if (!username || !password) {
115 return FALSE;
118 memset(&auth_identity, 0, sizeof(auth_identity));
119 auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
121 if (!is_empty(domain)) {
122 auth_identity.Domain = (unsigned char*)domain;
123 auth_identity.DomainLength = strlen(domain);
126 auth_identity.User = (unsigned char*)username;
127 auth_identity.UserLength = strlen(username);
129 auth_identity.Password = (unsigned char*)password;
130 auth_identity.PasswordLength = strlen(password);
133 ctx->cred_sspi = g_malloc0(sizeof(CredHandle));
135 ret = AcquireCredentialsHandleA(NULL,
136 (SEC_CHAR *)mech_names[ctx->type],
137 SECPKG_CRED_OUTBOUND,
138 NULL,
139 (context->flags & SIP_SEC_FLAG_COMMON_SSO) ? NULL : &auth_identity,
140 NULL,
141 NULL,
142 ctx->cred_sspi,
143 &expiry);
145 if (ret != SEC_E_OK) {
146 sip_sec_sspi_print_error("sip_sec_acquire_cred__sspi: AcquireCredentialsHandleA", ret);
147 g_free(ctx->cred_sspi);
148 ctx->cred_sspi = NULL;
149 return FALSE;
150 } else {
151 return TRUE;
155 static gboolean
156 sip_sec_init_sec_context__sspi(SipSecContext context,
157 SipSecBuffer in_buff,
158 SipSecBuffer *out_buff,
159 const gchar *service_name)
161 TimeStamp expiry;
162 SecBufferDesc input_desc, output_desc;
163 SecBuffer in_token, out_token;
164 SECURITY_STATUS ret;
165 ULONG req_flags;
166 ULONG ret_flags;
167 context_sspi ctx = (context_sspi)context;
168 CtxtHandle* out_context;
170 SIPE_DEBUG_INFO_NOFORMAT("sip_sec_init_sec_context__sspi: in use");
172 if (context->flags & SIP_SEC_FLAG_SSPI_SIP_NTLM) {
173 if (context->flags & SIP_SEC_FLAG_SSPI_INITIAL) {
174 /* empty initial message for connection-less NTLM */
175 if (in_buff.value == NULL) {
176 SIPE_DEBUG_INFO_NOFORMAT("sip_sec_init_sec_context__sspi: initial message for connection-less NTLM");
177 out_buff->length = 0;
178 out_buff->value = (guint8 *) g_strdup("");
179 return TRUE;
181 /* call again to create context for connection-less NTLM */
182 } else {
183 SipSecBuffer empty = { 0, NULL };
185 context->flags &= ~SIP_SEC_FLAG_SSPI_INITIAL;
186 if (sip_sec_init_sec_context__sspi(context,
187 empty,
188 out_buff,
189 service_name)) {
190 SIPE_DEBUG_INFO_NOFORMAT("sip_sec_init_sec_context__sspi: connection-less NTLM second round");
191 g_free(out_buff->value);
192 } else {
193 return FALSE;
197 } else if ((context->flags & SIP_SEC_FLAG_COMMON_HTTP) &&
198 ctx->ctx_sspi &&
199 (in_buff.value == NULL)) {
201 * We already have an initialized connection-based context
202 * and we're asked to initialize it with a NULL token. This
203 * will fail with "invalid token". Drop old context instead.
205 SIPE_DEBUG_INFO_NOFORMAT("sip_sec_init_sec_context__sspi: dropping old context");
206 DeleteSecurityContext(ctx->ctx_sspi);
207 g_free(ctx->ctx_sspi);
208 ctx->ctx_sspi = NULL;
211 /* reuse existing context on following calls */
212 out_context = ctx->ctx_sspi ? ctx->ctx_sspi : g_malloc0(sizeof(CtxtHandle));
214 input_desc.cBuffers = 1;
215 input_desc.pBuffers = &in_token;
216 input_desc.ulVersion = SECBUFFER_VERSION;
218 /* input token */
219 in_token.BufferType = SECBUFFER_TOKEN;
220 in_token.cbBuffer = in_buff.length;
221 in_token.pvBuffer = in_buff.value;
223 output_desc.cBuffers = 1;
224 output_desc.pBuffers = &out_token;
225 output_desc.ulVersion = SECBUFFER_VERSION;
227 /* to hold output token */
228 out_token.BufferType = SECBUFFER_TOKEN;
229 out_token.cbBuffer = 0;
230 out_token.pvBuffer = NULL;
232 req_flags = (ISC_REQ_ALLOCATE_MEMORY |
233 ISC_REQ_INTEGRITY |
234 ISC_REQ_IDENTIFY);
236 if (context->flags & SIP_SEC_FLAG_SSPI_SIP_NTLM) {
237 req_flags |= (ISC_REQ_DATAGRAM);
240 ret = InitializeSecurityContextA(ctx->cred_sspi,
241 ctx->ctx_sspi,
242 (SEC_CHAR *)service_name,
243 req_flags,
245 SECURITY_NATIVE_DREP,
246 &input_desc,
248 out_context,
249 &output_desc,
250 &ret_flags,
251 &expiry);
253 if (ret != SEC_E_OK && ret != SEC_I_CONTINUE_NEEDED) {
254 if (!ctx->ctx_sspi)
255 g_free(out_context);
256 sip_sec_destroy_sspi_context(ctx);
257 sip_sec_sspi_print_error("sip_sec_init_sec_context__sspi: InitializeSecurityContextA", ret);
258 return FALSE;
261 out_buff->length = out_token.cbBuffer;
262 out_buff->value = NULL;
263 if (out_token.cbBuffer) {
264 out_buff->value = g_malloc(out_token.cbBuffer);
265 memcpy(out_buff->value, out_token.pvBuffer, out_token.cbBuffer);
266 FreeContextBuffer(out_token.pvBuffer);
269 ctx->ctx_sspi = out_context;
271 if (ctx->type == SIPE_AUTHENTICATION_TYPE_KERBEROS) {
272 context->expires = sip_sec_get_interval_from_now_sec(expiry);
275 if (ret != SEC_I_CONTINUE_NEEDED) {
276 /* Authentication is completed */
277 context->flags |= SIP_SEC_FLAG_COMMON_READY;
280 return TRUE;
283 static void
284 sip_sec_destroy_sec_context__sspi(SipSecContext context)
286 sip_sec_destroy_sspi_context((context_sspi)context);
287 g_free(context);
291 * @param message a NULL terminated string to sign
294 static gboolean
295 sip_sec_make_signature__sspi(SipSecContext context,
296 const gchar *message,
297 SipSecBuffer *signature)
299 SecBufferDesc buffs_desc;
300 SecBuffer buffs[2];
301 SECURITY_STATUS ret;
302 SecPkgContext_Sizes context_sizes;
303 guchar *signature_buff;
304 size_t signature_buff_length;
305 context_sspi ctx = (context_sspi) context;
307 ret = QueryContextAttributes(ctx->ctx_sspi,
308 SECPKG_ATTR_SIZES,
309 &context_sizes);
311 if (ret != SEC_E_OK) {
312 sip_sec_sspi_print_error("sip_sec_make_signature__sspi: QueryContextAttributes", ret);
313 return FALSE;
316 signature_buff_length = context_sizes.cbMaxSignature;
317 signature_buff = g_malloc(signature_buff_length);
319 buffs_desc.cBuffers = 2;
320 buffs_desc.pBuffers = buffs;
321 buffs_desc.ulVersion = SECBUFFER_VERSION;
323 /* message to sign */
324 buffs[0].BufferType = SECBUFFER_DATA;
325 buffs[0].cbBuffer = strlen(message);
326 buffs[0].pvBuffer = (PVOID)message;
328 /* to hold signature */
329 buffs[1].BufferType = SECBUFFER_TOKEN;
330 buffs[1].cbBuffer = signature_buff_length;
331 buffs[1].pvBuffer = signature_buff;
333 ret = MakeSignature(ctx->ctx_sspi,
334 (ULONG)0,
335 &buffs_desc,
336 100);
337 if (ret != SEC_E_OK) {
338 sip_sec_sspi_print_error("sip_sec_make_signature__sspi: MakeSignature", ret);
339 g_free(signature_buff);
340 return FALSE;
343 signature->value = signature_buff;
344 signature->length = buffs[1].cbBuffer;
346 return TRUE;
350 * @param message a NULL terminated string to check signature of
351 * @return TRUE on success
353 static gboolean
354 sip_sec_verify_signature__sspi(SipSecContext context,
355 const gchar *message,
356 SipSecBuffer signature)
358 SecBufferDesc buffs_desc;
359 SecBuffer buffs[2];
360 SECURITY_STATUS ret;
362 buffs_desc.cBuffers = 2;
363 buffs_desc.pBuffers = buffs;
364 buffs_desc.ulVersion = SECBUFFER_VERSION;
366 /* message to sign */
367 buffs[0].BufferType = SECBUFFER_DATA;
368 buffs[0].cbBuffer = strlen(message);
369 buffs[0].pvBuffer = (PVOID)message;
371 /* signature to check */
372 buffs[1].BufferType = SECBUFFER_TOKEN;
373 buffs[1].cbBuffer = signature.length;
374 buffs[1].pvBuffer = signature.value;
376 ret = VerifySignature(((context_sspi)context)->ctx_sspi,
377 &buffs_desc,
381 if (ret != SEC_E_OK) {
382 sip_sec_sspi_print_error("sip_sec_verify_signature__sspi: VerifySignature", ret);
383 return FALSE;
386 return TRUE;
389 SipSecContext
390 sip_sec_create_context__sspi(guint type)
392 context_sspi context = g_malloc0(sizeof(struct _context_sspi));
393 if (!context) return(NULL);
395 context->common.acquire_cred_func = sip_sec_acquire_cred__sspi;
396 context->common.init_context_func = sip_sec_init_sec_context__sspi;
397 context->common.destroy_context_func = sip_sec_destroy_sec_context__sspi;
398 context->common.make_signature_func = sip_sec_make_signature__sspi;
399 context->common.verify_signature_func = sip_sec_verify_signature__sspi;
400 context->type = type;
402 return((SipSecContext) context);
405 gboolean sip_sec_password__sspi(void)
407 /* SSPI supports Single-Sign On */
408 return(FALSE);
411 /* Utility Functions */
414 * Returns interval in seconds from now till provided value
416 static int
417 sip_sec_get_interval_from_now_sec(TimeStamp timestamp)
419 SYSTEMTIME stNow;
420 FILETIME ftNow;
421 ULARGE_INTEGER uliNow, uliTo;
423 GetLocalTime(&stNow);
424 SystemTimeToFileTime(&stNow, &ftNow);
426 uliNow.LowPart = ftNow.dwLowDateTime;
427 uliNow.HighPart = ftNow.dwHighDateTime;
429 uliTo.LowPart = timestamp.LowPart;
430 uliTo.HighPart = timestamp.HighPart;
432 return (int)((uliTo.QuadPart - uliNow.QuadPart)/10/1000/1000);
435 static void
436 sip_sec_sspi_print_error(const gchar *func,
437 SECURITY_STATUS ret)
439 gchar *error_message;
440 static char *buff;
441 guint buff_length;
443 buff_length = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
444 FORMAT_MESSAGE_ALLOCATE_BUFFER |
445 FORMAT_MESSAGE_IGNORE_INSERTS,
447 ret,
449 (LPTSTR)&buff,
450 16384,
452 error_message = g_strndup(buff, buff_length);
453 LocalFree(buff);
455 SIPE_DEBUG_ERROR("SSPI ERROR [%d] in %s: %s", (int)ret, func, error_message);
456 g_free(error_message);
461 Local Variables:
462 mode: c
463 c-file-style: "bsd"
464 indent-tabs-mode: t
465 tab-width: 8
466 End: