ucs: fix Persona key extraction
[siplcs.git] / src / core / sip-sec-sspi.c
blobfe9b76e5d0bcb14e9f276e25debfef4a2f907f45
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 "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 */
58 #ifndef ISC_REQ_IDENTIFY
59 #define ISC_REQ_IDENTIFY 0x00002000
60 #endif
62 typedef struct _context_sspi {
63 struct sip_sec_context common;
64 CredHandle* cred_sspi;
65 CtxtHandle* ctx_sspi;
66 } *context_sspi;
68 #define SIP_SEC_FLAG_SSPI_SIP_NTLM 0x00010000
70 /* Utility Functions */
72 static void
73 sip_sec_sspi_print_error(const gchar *func,
74 SECURITY_STATUS ret)
76 gchar *error_message;
77 static char *buff;
78 guint buff_length;
80 buff_length = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
81 FORMAT_MESSAGE_ALLOCATE_BUFFER |
82 FORMAT_MESSAGE_IGNORE_INSERTS,
84 ret,
86 (LPTSTR)&buff,
87 16384,
88 0);
89 error_message = g_strndup(buff, buff_length);
90 LocalFree(buff);
92 SIPE_DEBUG_ERROR("SSPI ERROR [%d] in %s: %s", (int)ret, func, error_message);
93 g_free(error_message);
96 /* Returns interval in seconds from now till provided value */
97 static guint
98 sip_sec_get_interval_from_now_sec(TimeStamp timestamp)
100 SYSTEMTIME stNow;
101 FILETIME ftNow;
102 ULARGE_INTEGER uliNow, uliTo;
104 GetLocalTime(&stNow);
105 SystemTimeToFileTime(&stNow, &ftNow);
107 uliNow.LowPart = ftNow.dwLowDateTime;
108 uliNow.HighPart = ftNow.dwHighDateTime;
110 uliTo.LowPart = timestamp.LowPart;
111 uliTo.HighPart = timestamp.HighPart;
113 return((uliTo.QuadPart - uliNow.QuadPart)/10/1000/1000);
116 static void
117 sip_sec_destroy_sspi_context(context_sspi context)
119 if (context->ctx_sspi) {
120 DeleteSecurityContext(context->ctx_sspi);
121 g_free(context->ctx_sspi);
122 context->ctx_sspi = NULL;
124 if (context->cred_sspi) {
125 FreeCredentialsHandle(context->cred_sspi);
126 g_free(context->cred_sspi);
127 context->cred_sspi = NULL;
131 /* sip-sec-mech.h API implementation for SSPI - Kerberos, NTLM and Negotiate */
133 static gboolean
134 sip_sec_acquire_cred__sspi(SipSecContext context,
135 const gchar *domain,
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;
144 /* this is the first time we are allowed to set private flags */
145 if (((context->flags & SIP_SEC_FLAG_COMMON_HTTP) == 0) &&
146 (context->type == SIPE_AUTHENTICATION_TYPE_NTLM))
147 context->flags |= SIP_SEC_FLAG_SSPI_SIP_NTLM;
149 if ((context->flags & SIP_SEC_FLAG_COMMON_SSO) == 0) {
150 if (!username || !password) {
151 return FALSE;
154 memset(&auth_identity, 0, sizeof(auth_identity));
155 auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
157 if (!is_empty(domain)) {
158 auth_identity.Domain = (unsigned char*)domain;
159 auth_identity.DomainLength = strlen(domain);
162 auth_identity.User = (unsigned char*)username;
163 auth_identity.UserLength = strlen(username);
165 auth_identity.Password = (unsigned char*)password;
166 auth_identity.PasswordLength = strlen(password);
169 ctx->cred_sspi = g_malloc0(sizeof(CredHandle));
171 ret = AcquireCredentialsHandleA(NULL,
172 (SEC_CHAR *)mech_names[context->type],
173 SECPKG_CRED_OUTBOUND,
174 NULL,
175 (context->flags & SIP_SEC_FLAG_COMMON_SSO) ? NULL : &auth_identity,
176 NULL,
177 NULL,
178 ctx->cred_sspi,
179 &expiry);
181 if (ret != SEC_E_OK) {
182 sip_sec_sspi_print_error("sip_sec_acquire_cred__sspi: AcquireCredentialsHandleA", ret);
183 g_free(ctx->cred_sspi);
184 ctx->cred_sspi = NULL;
185 return FALSE;
186 } else {
187 return TRUE;
191 static gboolean
192 sip_sec_init_sec_context__sspi(SipSecContext context,
193 SipSecBuffer in_buff,
194 SipSecBuffer *out_buff,
195 const gchar *service_name)
197 TimeStamp expiry;
198 SecBufferDesc input_desc, output_desc;
199 SecBuffer in_token, out_token;
200 SECURITY_STATUS ret;
201 ULONG req_flags;
202 ULONG ret_flags;
203 context_sspi ctx = (context_sspi)context;
204 CtxtHandle* out_context;
206 SIPE_DEBUG_INFO_NOFORMAT("sip_sec_init_sec_context__sspi: in use");
209 * If authentication was already completed, then this mean a new
210 * authentication handshake has started on the existing connection.
211 * We must throw away the old context, because we need a new one.
213 if ((context->flags & SIP_SEC_FLAG_COMMON_READY) &&
214 ctx->ctx_sspi) {
215 SIPE_DEBUG_INFO_NOFORMAT("sip_sec_init_sec_context__sspi: dropping old context");
216 DeleteSecurityContext(ctx->ctx_sspi);
217 g_free(ctx->ctx_sspi);
218 ctx->ctx_sspi = NULL;
219 context->flags &= ~SIP_SEC_FLAG_COMMON_READY;
222 /* reuse existing context on following calls */
223 out_context = ctx->ctx_sspi ? ctx->ctx_sspi : g_malloc0(sizeof(CtxtHandle));
225 input_desc.cBuffers = 1;
226 input_desc.pBuffers = &in_token;
227 input_desc.ulVersion = SECBUFFER_VERSION;
229 /* input token */
230 in_token.BufferType = SECBUFFER_TOKEN;
231 in_token.cbBuffer = in_buff.length;
232 in_token.pvBuffer = in_buff.value;
234 output_desc.cBuffers = 1;
235 output_desc.pBuffers = &out_token;
236 output_desc.ulVersion = SECBUFFER_VERSION;
238 /* to hold output token */
239 out_token.BufferType = SECBUFFER_TOKEN;
240 out_token.cbBuffer = 0;
241 out_token.pvBuffer = NULL;
243 req_flags = (ISC_REQ_ALLOCATE_MEMORY |
244 ISC_REQ_INTEGRITY |
245 ISC_REQ_IDENTIFY);
247 if (context->flags & SIP_SEC_FLAG_SSPI_SIP_NTLM) {
248 req_flags |= (ISC_REQ_DATAGRAM);
251 ret = InitializeSecurityContextA(ctx->cred_sspi,
252 ctx->ctx_sspi,
253 (SEC_CHAR *)service_name,
254 req_flags,
256 SECURITY_NATIVE_DREP,
257 &input_desc,
259 out_context,
260 &output_desc,
261 &ret_flags,
262 &expiry);
264 if (ret != SEC_E_OK && ret != SEC_I_CONTINUE_NEEDED) {
265 if (!ctx->ctx_sspi)
266 g_free(out_context);
267 sip_sec_destroy_sspi_context(ctx);
268 sip_sec_sspi_print_error("sip_sec_init_sec_context__sspi: InitializeSecurityContextA", ret);
269 return FALSE;
272 out_buff->length = out_token.cbBuffer;
273 if (out_token.cbBuffer) {
274 out_buff->value = g_malloc(out_token.cbBuffer);
275 memcpy(out_buff->value, out_token.pvBuffer, out_token.cbBuffer);
276 } else {
277 /* Special case: empty token */
278 out_buff->value = (guint8 *) g_strdup("");
280 FreeContextBuffer(out_token.pvBuffer);
282 ctx->ctx_sspi = out_context;
284 if (context->type == SIPE_AUTHENTICATION_TYPE_KERBEROS) {
285 context->expires = sip_sec_get_interval_from_now_sec(expiry);
288 if (ret != SEC_I_CONTINUE_NEEDED) {
289 /* Authentication is completed */
290 context->flags |= SIP_SEC_FLAG_COMMON_READY;
293 return TRUE;
296 static void
297 sip_sec_destroy_sec_context__sspi(SipSecContext context)
299 sip_sec_destroy_sspi_context((context_sspi)context);
300 g_free(context);
304 * @param message a NULL terminated string to sign
307 static gboolean
308 sip_sec_make_signature__sspi(SipSecContext context,
309 const gchar *message,
310 SipSecBuffer *signature)
312 SecBufferDesc buffs_desc;
313 SecBuffer buffs[2];
314 SECURITY_STATUS ret;
315 SecPkgContext_Sizes context_sizes;
316 guchar *signature_buff;
317 size_t signature_buff_length;
318 context_sspi ctx = (context_sspi) context;
320 ret = QueryContextAttributes(ctx->ctx_sspi,
321 SECPKG_ATTR_SIZES,
322 &context_sizes);
324 if (ret != SEC_E_OK) {
325 sip_sec_sspi_print_error("sip_sec_make_signature__sspi: QueryContextAttributes", ret);
326 return FALSE;
329 signature_buff_length = context_sizes.cbMaxSignature;
330 signature_buff = g_malloc(signature_buff_length);
332 buffs_desc.cBuffers = 2;
333 buffs_desc.pBuffers = buffs;
334 buffs_desc.ulVersion = SECBUFFER_VERSION;
336 /* message to sign */
337 buffs[0].BufferType = SECBUFFER_DATA;
338 buffs[0].cbBuffer = strlen(message);
339 buffs[0].pvBuffer = (PVOID)message;
341 /* to hold signature */
342 buffs[1].BufferType = SECBUFFER_TOKEN;
343 buffs[1].cbBuffer = signature_buff_length;
344 buffs[1].pvBuffer = signature_buff;
346 ret = MakeSignature(ctx->ctx_sspi,
347 (ULONG)0,
348 &buffs_desc,
349 100);
350 if (ret != SEC_E_OK) {
351 sip_sec_sspi_print_error("sip_sec_make_signature__sspi: MakeSignature", ret);
352 g_free(signature_buff);
353 return FALSE;
356 signature->value = signature_buff;
357 signature->length = buffs[1].cbBuffer;
359 return TRUE;
363 * @param message a NULL terminated string to check signature of
364 * @return TRUE on success
366 static gboolean
367 sip_sec_verify_signature__sspi(SipSecContext context,
368 const gchar *message,
369 SipSecBuffer signature)
371 SecBufferDesc buffs_desc;
372 SecBuffer buffs[2];
373 SECURITY_STATUS ret;
375 buffs_desc.cBuffers = 2;
376 buffs_desc.pBuffers = buffs;
377 buffs_desc.ulVersion = SECBUFFER_VERSION;
379 /* message to sign */
380 buffs[0].BufferType = SECBUFFER_DATA;
381 buffs[0].cbBuffer = strlen(message);
382 buffs[0].pvBuffer = (PVOID)message;
384 /* signature to check */
385 buffs[1].BufferType = SECBUFFER_TOKEN;
386 buffs[1].cbBuffer = signature.length;
387 buffs[1].pvBuffer = signature.value;
389 ret = VerifySignature(((context_sspi)context)->ctx_sspi,
390 &buffs_desc,
394 if (ret != SEC_E_OK) {
395 sip_sec_sspi_print_error("sip_sec_verify_signature__sspi: VerifySignature", ret);
396 return FALSE;
399 return TRUE;
402 /* SSPI implements SPNEGO (RFC 4559) */
403 static const gchar *
404 sip_sec_context_name__sspi(SipSecContext context)
406 return(mech_names[context->type]);
409 SipSecContext
410 sip_sec_create_context__sspi(SIPE_UNUSED_PARAMETER guint type)
412 context_sspi context = g_malloc0(sizeof(struct _context_sspi));
413 if (!context) return(NULL);
415 context->common.acquire_cred_func = sip_sec_acquire_cred__sspi;
416 context->common.init_context_func = sip_sec_init_sec_context__sspi;
417 context->common.destroy_context_func = sip_sec_destroy_sec_context__sspi;
418 context->common.make_signature_func = sip_sec_make_signature__sspi;
419 context->common.verify_signature_func = sip_sec_verify_signature__sspi;
420 context->common.context_name_func = sip_sec_context_name__sspi;
422 return((SipSecContext) context);
425 gboolean sip_sec_password__sspi(void)
427 /* SSPI supports Single-Sign On */
428 return(FALSE);
432 Local Variables:
433 mode: c
434 c-file-style: "bsd"
435 indent-tabs-mode: t
436 tab-width: 8
437 End: