git: ignore Xcode project artifacts
[siplcs.git] / src / core / sip-sec-sspi.c
blob28d2138032b43c33f5f07bdd1dc6dee08e69fc44
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 "", /* SIPE_AUTHENTICATION_TYPE_BASIC */
51 "NTLM", /* SIPE_AUTHENTICATION_TYPE_NTLM */
52 "Kerberos", /* SIPE_AUTHENTICATION_TYPE_KERBEROS */
53 "Negotiate", /* SIPE_AUTHENTICATION_TYPE_NEGOTIATE */
54 "", /* SIPE_AUTHENTICATION_TYPE_TLS_DSK */
57 #ifndef ISC_REQ_IDENTIFY
58 #define ISC_REQ_IDENTIFY 0x00002000
59 #endif
61 typedef struct _context_sspi {
62 struct sip_sec_context common;
63 CredHandle* cred_sspi;
64 CtxtHandle* ctx_sspi;
65 guint type;
66 } *context_sspi;
68 #define SIP_SEC_FLAG_SSPI_INITIAL 0x00010000
69 #define SIP_SEC_FLAG_SSPI_SIP_NTLM 0x00020000
71 static int
72 sip_sec_get_interval_from_now_sec(TimeStamp timestamp);
74 static void
75 sip_sec_sspi_print_error(const gchar *func,
76 SECURITY_STATUS ret);
78 /** internal method */
79 static void
80 sip_sec_destroy_sspi_context(context_sspi context)
82 if (context->ctx_sspi) {
83 DeleteSecurityContext(context->ctx_sspi);
84 g_free(context->ctx_sspi);
85 context->ctx_sspi = NULL;
87 if (context->cred_sspi) {
88 FreeCredentialsHandle(context->cred_sspi);
89 g_free(context->cred_sspi);
90 context->cred_sspi = NULL;
94 /* sip-sec-mech.h API implementation for SSPI - Kerberos and NTLM */
96 static gboolean
97 sip_sec_acquire_cred__sspi(SipSecContext context,
98 const gchar *domain,
99 const gchar *username,
100 const gchar *password)
102 SECURITY_STATUS ret;
103 TimeStamp expiry;
104 SEC_WINNT_AUTH_IDENTITY auth_identity;
105 context_sspi ctx = (context_sspi)context;
107 /* this is the first time we are allowed to set private flags */
108 context->flags |= SIP_SEC_FLAG_SSPI_INITIAL;
110 if (((context->flags & SIP_SEC_FLAG_COMMON_HTTP) == 0) &&
111 (ctx->type == SIPE_AUTHENTICATION_TYPE_NTLM))
112 context->flags |= SIP_SEC_FLAG_SSPI_SIP_NTLM;
114 if ((context->flags & SIP_SEC_FLAG_COMMON_SSO) == 0) {
115 if (!username || !password) {
116 return FALSE;
119 memset(&auth_identity, 0, sizeof(auth_identity));
120 auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
122 if (!is_empty(domain)) {
123 auth_identity.Domain = (unsigned char*)domain;
124 auth_identity.DomainLength = strlen(domain);
127 auth_identity.User = (unsigned char*)username;
128 auth_identity.UserLength = strlen(username);
130 auth_identity.Password = (unsigned char*)password;
131 auth_identity.PasswordLength = strlen(password);
134 ctx->cred_sspi = g_malloc0(sizeof(CredHandle));
136 ret = AcquireCredentialsHandleA(NULL,
137 (SEC_CHAR *)mech_names[ctx->type],
138 SECPKG_CRED_OUTBOUND,
139 NULL,
140 (context->flags & SIP_SEC_FLAG_COMMON_SSO) ? NULL : &auth_identity,
141 NULL,
142 NULL,
143 ctx->cred_sspi,
144 &expiry);
146 if (ret != SEC_E_OK) {
147 sip_sec_sspi_print_error("sip_sec_acquire_cred__sspi: AcquireCredentialsHandleA", ret);
148 g_free(ctx->cred_sspi);
149 ctx->cred_sspi = NULL;
150 return FALSE;
151 } else {
152 return TRUE;
156 static gboolean
157 sip_sec_init_sec_context__sspi(SipSecContext context,
158 SipSecBuffer in_buff,
159 SipSecBuffer *out_buff,
160 const gchar *service_name)
162 TimeStamp expiry;
163 SecBufferDesc input_desc, output_desc;
164 SecBuffer in_token, out_token;
165 SECURITY_STATUS ret;
166 ULONG req_flags;
167 ULONG ret_flags;
168 context_sspi ctx = (context_sspi)context;
169 CtxtHandle* out_context;
171 SIPE_DEBUG_INFO_NOFORMAT("sip_sec_init_sec_context__sspi: in use");
173 if (context->flags & SIP_SEC_FLAG_SSPI_SIP_NTLM) {
174 if (context->flags & SIP_SEC_FLAG_SSPI_INITIAL) {
175 /* empty initial message for connection-less NTLM */
176 if (in_buff.value == NULL) {
177 SIPE_DEBUG_INFO_NOFORMAT("sip_sec_init_sec_context__sspi: initial message for connection-less NTLM");
178 out_buff->length = 0;
179 out_buff->value = (guint8 *) g_strdup("");
180 return TRUE;
182 /* call again to create context for connection-less NTLM */
183 } else {
184 SipSecBuffer empty = { 0, NULL };
186 context->flags &= ~SIP_SEC_FLAG_SSPI_INITIAL;
187 if (sip_sec_init_sec_context__sspi(context,
188 empty,
189 out_buff,
190 service_name)) {
191 SIPE_DEBUG_INFO_NOFORMAT("sip_sec_init_sec_context__sspi: connection-less NTLM second round");
192 g_free(out_buff->value);
193 } else {
194 return FALSE;
198 } else if ((context->flags & SIP_SEC_FLAG_COMMON_HTTP) &&
199 ctx->ctx_sspi &&
200 (in_buff.value == NULL)) {
202 * We already have an initialized connection-based context
203 * and we're asked to initialize it with a NULL token. This
204 * will fail with "invalid token". Drop old context instead.
206 SIPE_DEBUG_INFO_NOFORMAT("sip_sec_init_sec_context__sspi: dropping old context");
207 DeleteSecurityContext(ctx->ctx_sspi);
208 g_free(ctx->ctx_sspi);
209 ctx->ctx_sspi = NULL;
212 /* reuse existing context on following calls */
213 out_context = ctx->ctx_sspi ? ctx->ctx_sspi : g_malloc0(sizeof(CtxtHandle));
215 input_desc.cBuffers = 1;
216 input_desc.pBuffers = &in_token;
217 input_desc.ulVersion = SECBUFFER_VERSION;
219 /* input token */
220 in_token.BufferType = SECBUFFER_TOKEN;
221 in_token.cbBuffer = in_buff.length;
222 in_token.pvBuffer = in_buff.value;
224 output_desc.cBuffers = 1;
225 output_desc.pBuffers = &out_token;
226 output_desc.ulVersion = SECBUFFER_VERSION;
228 /* to hold output token */
229 out_token.BufferType = SECBUFFER_TOKEN;
230 out_token.cbBuffer = 0;
231 out_token.pvBuffer = NULL;
233 req_flags = (ISC_REQ_ALLOCATE_MEMORY |
234 ISC_REQ_INTEGRITY |
235 ISC_REQ_IDENTIFY);
237 if (context->flags & SIP_SEC_FLAG_SSPI_SIP_NTLM) {
238 req_flags |= (ISC_REQ_DATAGRAM);
241 ret = InitializeSecurityContextA(ctx->cred_sspi,
242 ctx->ctx_sspi,
243 (SEC_CHAR *)service_name,
244 req_flags,
246 SECURITY_NATIVE_DREP,
247 &input_desc,
249 out_context,
250 &output_desc,
251 &ret_flags,
252 &expiry);
254 if (ret != SEC_E_OK && ret != SEC_I_CONTINUE_NEEDED) {
255 if (!ctx->ctx_sspi)
256 g_free(out_context);
257 sip_sec_destroy_sspi_context(ctx);
258 sip_sec_sspi_print_error("sip_sec_init_sec_context__sspi: InitializeSecurityContextA", ret);
259 return FALSE;
262 out_buff->length = out_token.cbBuffer;
263 out_buff->value = NULL;
264 if (out_token.cbBuffer) {
265 out_buff->value = g_malloc(out_token.cbBuffer);
266 memcpy(out_buff->value, out_token.pvBuffer, out_token.cbBuffer);
267 FreeContextBuffer(out_token.pvBuffer);
270 ctx->ctx_sspi = out_context;
272 if (ctx->type == SIPE_AUTHENTICATION_TYPE_KERBEROS) {
273 context->expires = sip_sec_get_interval_from_now_sec(expiry);
276 if (ret != SEC_I_CONTINUE_NEEDED) {
277 /* Authentication is completed */
278 context->flags |= SIP_SEC_FLAG_COMMON_READY;
281 return TRUE;
284 static void
285 sip_sec_destroy_sec_context__sspi(SipSecContext context)
287 sip_sec_destroy_sspi_context((context_sspi)context);
288 g_free(context);
292 * @param message a NULL terminated string to sign
295 static gboolean
296 sip_sec_make_signature__sspi(SipSecContext context,
297 const gchar *message,
298 SipSecBuffer *signature)
300 SecBufferDesc buffs_desc;
301 SecBuffer buffs[2];
302 SECURITY_STATUS ret;
303 SecPkgContext_Sizes context_sizes;
304 guchar *signature_buff;
305 size_t signature_buff_length;
306 context_sspi ctx = (context_sspi) context;
308 ret = QueryContextAttributes(ctx->ctx_sspi,
309 SECPKG_ATTR_SIZES,
310 &context_sizes);
312 if (ret != SEC_E_OK) {
313 sip_sec_sspi_print_error("sip_sec_make_signature__sspi: QueryContextAttributes", ret);
314 return FALSE;
317 signature_buff_length = context_sizes.cbMaxSignature;
318 signature_buff = g_malloc(signature_buff_length);
320 buffs_desc.cBuffers = 2;
321 buffs_desc.pBuffers = buffs;
322 buffs_desc.ulVersion = SECBUFFER_VERSION;
324 /* message to sign */
325 buffs[0].BufferType = SECBUFFER_DATA;
326 buffs[0].cbBuffer = strlen(message);
327 buffs[0].pvBuffer = (PVOID)message;
329 /* to hold signature */
330 buffs[1].BufferType = SECBUFFER_TOKEN;
331 buffs[1].cbBuffer = signature_buff_length;
332 buffs[1].pvBuffer = signature_buff;
334 ret = MakeSignature(ctx->ctx_sspi,
335 (ULONG)0,
336 &buffs_desc,
337 100);
338 if (ret != SEC_E_OK) {
339 sip_sec_sspi_print_error("sip_sec_make_signature__sspi: MakeSignature", ret);
340 g_free(signature_buff);
341 return FALSE;
344 signature->value = signature_buff;
345 signature->length = buffs[1].cbBuffer;
347 return TRUE;
351 * @param message a NULL terminated string to check signature of
352 * @return TRUE on success
354 static gboolean
355 sip_sec_verify_signature__sspi(SipSecContext context,
356 const gchar *message,
357 SipSecBuffer signature)
359 SecBufferDesc buffs_desc;
360 SecBuffer buffs[2];
361 SECURITY_STATUS ret;
363 buffs_desc.cBuffers = 2;
364 buffs_desc.pBuffers = buffs;
365 buffs_desc.ulVersion = SECBUFFER_VERSION;
367 /* message to sign */
368 buffs[0].BufferType = SECBUFFER_DATA;
369 buffs[0].cbBuffer = strlen(message);
370 buffs[0].pvBuffer = (PVOID)message;
372 /* signature to check */
373 buffs[1].BufferType = SECBUFFER_TOKEN;
374 buffs[1].cbBuffer = signature.length;
375 buffs[1].pvBuffer = signature.value;
377 ret = VerifySignature(((context_sspi)context)->ctx_sspi,
378 &buffs_desc,
382 if (ret != SEC_E_OK) {
383 sip_sec_sspi_print_error("sip_sec_verify_signature__sspi: VerifySignature", ret);
384 return FALSE;
387 return TRUE;
390 SipSecContext
391 sip_sec_create_context__sspi(guint type)
393 context_sspi context = g_malloc0(sizeof(struct _context_sspi));
394 if (!context) return(NULL);
396 context->common.acquire_cred_func = sip_sec_acquire_cred__sspi;
397 context->common.init_context_func = sip_sec_init_sec_context__sspi;
398 context->common.destroy_context_func = sip_sec_destroy_sec_context__sspi;
399 context->common.make_signature_func = sip_sec_make_signature__sspi;
400 context->common.verify_signature_func = sip_sec_verify_signature__sspi;
401 context->type = type;
403 return((SipSecContext) context);
406 gboolean sip_sec_password__sspi(void)
408 /* SSPI supports Single-Sign On */
409 return(FALSE);
412 /* Utility Functions */
415 * Returns interval in seconds from now till provided value
417 static int
418 sip_sec_get_interval_from_now_sec(TimeStamp timestamp)
420 SYSTEMTIME stNow;
421 FILETIME ftNow;
422 ULARGE_INTEGER uliNow, uliTo;
424 GetLocalTime(&stNow);
425 SystemTimeToFileTime(&stNow, &ftNow);
427 uliNow.LowPart = ftNow.dwLowDateTime;
428 uliNow.HighPart = ftNow.dwHighDateTime;
430 uliTo.LowPart = timestamp.LowPart;
431 uliTo.HighPart = timestamp.HighPart;
433 return (int)((uliTo.QuadPart - uliNow.QuadPart)/10/1000/1000);
436 static void
437 sip_sec_sspi_print_error(const gchar *func,
438 SECURITY_STATUS ret)
440 gchar *error_message;
441 static char *buff;
442 guint buff_length;
444 buff_length = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
445 FORMAT_MESSAGE_ALLOCATE_BUFFER |
446 FORMAT_MESSAGE_IGNORE_INSERTS,
448 ret,
450 (LPTSTR)&buff,
451 16384,
453 error_message = g_strndup(buff, buff_length);
454 LocalFree(buff);
456 SIPE_DEBUG_ERROR("SSPI ERROR [%d] in %s: %s", (int)ret, func, error_message);
457 g_free(error_message);
462 Local Variables:
463 mode: c
464 c-file-style: "bsd"
465 indent-tabs-mode: t
466 tab-width: 8
467 End: