security: fix memory leaks in SSPI
[siplcs.git] / src / core / sip-sec-sspi.c
blob251e50fa7f423437bee0c8485bfb5c03c2832a6f
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 #include <windows.h>
26 #include <rpc.h>
27 #ifndef SECURITY_WIN32
28 #define SECURITY_WIN32 1
29 #endif
30 #include <security.h>
32 #include <glib.h>
34 #include "sip-sec.h"
35 #include "sip-sec-mech.h"
36 #include "sip-sec-sspi.h"
37 #include "sipe-backend.h"
38 #include "sipe-core.h"
40 /* Mechanism names */
41 static const gchar * const mech_names[] = {
42 "", /* SIPE_AUTHENTICATION_TYPE_UNSET */
43 "NTLM", /* SIPE_AUTHENTICATION_TYPE_NTLM */
44 "Kerberos", /* SIPE_AUTHENTICATION_TYPE_KERBEROS */
45 "Negotiate", /* SIPE_AUTHENTICATION_TYPE_NEGOTIATE */
46 /* SSL/TLS provider, is this correct? */
47 "Schannel", /* SIPE_AUTHENTICATION_TYPE_TLS_DSK */
50 #ifndef ISC_REQ_IDENTIFY
51 #define ISC_REQ_IDENTIFY 0x00002000
52 #endif
54 typedef struct _context_sspi {
55 struct sip_sec_context common;
56 CredHandle* cred_sspi;
57 CtxtHandle* ctx_sspi;
58 guint type;
59 gboolean initial;
60 gboolean connection_less_ntlm;
61 } *context_sspi;
63 static int
64 sip_sec_get_interval_from_now_sec(TimeStamp timestamp);
66 static void
67 sip_sec_sspi_print_error(const char *func,
68 SECURITY_STATUS ret);
70 /** internal method */
71 static void
72 sip_sec_destroy_sspi_context(context_sspi context)
74 if (context->ctx_sspi) {
75 DeleteSecurityContext(context->ctx_sspi);
76 g_free(context->ctx_sspi);
77 context->ctx_sspi = NULL;
79 if (context->cred_sspi) {
80 FreeCredentialsHandle(context->cred_sspi);
81 g_free(context->cred_sspi);
82 context->cred_sspi = NULL;
86 /* sip-sec-mech.h API implementation for SSPI - Kerberos and NTLM */
88 static sip_uint32
89 sip_sec_acquire_cred__sspi(SipSecContext context,
90 const char *domain,
91 const char *username,
92 const char *password)
94 SECURITY_STATUS ret;
95 TimeStamp expiry;
96 SEC_WINNT_AUTH_IDENTITY auth_identity;
97 context_sspi ctx = (context_sspi)context;
99 ctx->connection_less_ntlm = !context->is_connection_based &&
100 (ctx->type == SIPE_AUTHENTICATION_TYPE_NTLM);
102 if (!context->sso) {
103 if (!username || !password) {
104 return SIP_SEC_E_INTERNAL_ERROR;
107 memset(&auth_identity, 0, sizeof(auth_identity));
108 auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
110 if ( domain && (strlen(domain) > 0) ) {
111 auth_identity.Domain = (unsigned char*)domain;
112 auth_identity.DomainLength = strlen(domain);
115 auth_identity.User = (unsigned char*)username;
116 auth_identity.UserLength = strlen(username);
118 auth_identity.Password = (unsigned char*)password;
119 auth_identity.PasswordLength = strlen(password);
122 ctx->cred_sspi = g_malloc0(sizeof(CredHandle));
124 /* @TODO: this does not work for "Schannel" (TLS-DSK) as it expects
125 a SCHANNEL_CRED datastructure, pointing to the private key
126 and the client certificate */
127 ret = AcquireCredentialsHandleA(NULL,
128 (SEC_CHAR *)mech_names[ctx->type],
129 SECPKG_CRED_OUTBOUND,
130 NULL,
131 context->sso ? NULL : &auth_identity,
132 NULL,
133 NULL,
134 ctx->cred_sspi,
135 &expiry);
137 if (ret != SEC_E_OK) {
138 sip_sec_sspi_print_error("sip_sec_acquire_cred__sspi: AcquireCredentialsHandleA", ret);
139 g_free(ctx->cred_sspi);
140 ctx->cred_sspi = NULL;
141 return SIP_SEC_E_INTERNAL_ERROR;
142 } else {
143 return SIP_SEC_E_OK;
147 static sip_uint32
148 sip_sec_init_sec_context__sspi(SipSecContext context,
149 SipSecBuffer in_buff,
150 SipSecBuffer *out_buff,
151 const char *service_name)
153 TimeStamp expiry;
154 SecBufferDesc input_desc, output_desc;
155 SecBuffer in_token, out_token;
156 SECURITY_STATUS ret;
157 ULONG req_flags;
158 ULONG ret_flags;
159 context_sspi ctx = (context_sspi)context;
160 CtxtHandle* out_context;
162 SIPE_DEBUG_INFO_NOFORMAT("sip_sec_init_sec_context__sspi: in use");
164 if (ctx->connection_less_ntlm) {
165 if (ctx->initial) {
166 /* empty initial message for connection-less NTLM */
167 if (in_buff.value == NULL) {
168 SIPE_DEBUG_INFO_NOFORMAT("sip_sec_init_sec_context__sspi: initial message for connection-less NTLM");
169 out_buff->length = 0;
170 out_buff->value = (guint8 *) g_strdup("");
171 return SIP_SEC_E_OK;
173 /* call again to create context for connection-less NTLM */
174 } else {
175 SipSecBuffer empty = { 0, NULL };
177 ctx->initial = FALSE;
178 ret = sip_sec_init_sec_context__sspi(context,
179 empty,
180 out_buff,
181 service_name);
182 if (ret == SEC_E_OK) {
183 SIPE_DEBUG_INFO_NOFORMAT("sip_sec_init_sec_context__sspi: connection-less NTLM second round");
184 g_free(out_buff->value);
185 } else {
186 sip_sec_sspi_print_error("sip_sec_init_sec_context__sspi: unexpected NTLM state", ret);
187 return SIP_SEC_E_INTERNAL_ERROR;
191 } else if (context->is_connection_based &&
192 ctx->ctx_sspi &&
193 (in_buff.value == NULL)) {
195 * We already have an initialized connection-based context
196 * and we're asked to initialize it with a NULL token. This
197 * will fail with "invalid token". Drop old context instead.
199 SIPE_DEBUG_INFO_NOFORMAT("sip_sec_init_sec_context__sspi: dropping old context");
200 DeleteSecurityContext(ctx->ctx_sspi);
201 g_free(ctx->ctx_sspi);
202 ctx->ctx_sspi = NULL;
205 /* reuse existing context on following calls */
206 out_context = ctx->ctx_sspi ? ctx->ctx_sspi : g_malloc0(sizeof(CtxtHandle));
208 input_desc.cBuffers = 1;
209 input_desc.pBuffers = &in_token;
210 input_desc.ulVersion = SECBUFFER_VERSION;
212 /* input token */
213 in_token.BufferType = SECBUFFER_TOKEN;
214 in_token.cbBuffer = in_buff.length;
215 in_token.pvBuffer = in_buff.value;
217 output_desc.cBuffers = 1;
218 output_desc.pBuffers = &out_token;
219 output_desc.ulVersion = SECBUFFER_VERSION;
221 /* to hold output token */
222 out_token.BufferType = SECBUFFER_TOKEN;
223 out_token.cbBuffer = 0;
224 out_token.pvBuffer = NULL;
226 req_flags = (ISC_REQ_ALLOCATE_MEMORY |
227 ISC_REQ_INTEGRITY |
228 ISC_REQ_IDENTIFY);
230 if (ctx->connection_less_ntlm) {
231 req_flags |= (ISC_REQ_DATAGRAM);
234 ret = InitializeSecurityContextA(ctx->cred_sspi,
235 ctx->ctx_sspi,
236 (SEC_CHAR *)service_name,
237 req_flags,
239 SECURITY_NATIVE_DREP,
240 &input_desc,
242 out_context,
243 &output_desc,
244 &ret_flags,
245 &expiry);
247 if (ret != SEC_E_OK && ret != SEC_I_CONTINUE_NEEDED) {
248 if (!ctx->ctx_sspi)
249 g_free(out_context);
250 sip_sec_destroy_sspi_context(ctx);
251 sip_sec_sspi_print_error("sip_sec_init_sec_context__sspi: InitializeSecurityContextA", ret);
252 return SIP_SEC_E_INTERNAL_ERROR;
255 out_buff->length = out_token.cbBuffer;
256 out_buff->value = NULL;
257 if (out_token.cbBuffer) {
258 out_buff->value = g_malloc(out_token.cbBuffer);
259 memcpy(out_buff->value, out_token.pvBuffer, out_token.cbBuffer);
260 FreeContextBuffer(out_token.pvBuffer);
263 ctx->ctx_sspi = out_context;
265 if (ctx->type == SIPE_AUTHENTICATION_TYPE_KERBEROS) {
266 context->expires = sip_sec_get_interval_from_now_sec(expiry);
269 if (ret != SEC_I_CONTINUE_NEEDED) {
270 /* Authentication is completed */
271 ctx->common.is_ready = TRUE;
274 return SIP_SEC_E_OK;
277 static void
278 sip_sec_destroy_sec_context__sspi(SipSecContext context)
280 sip_sec_destroy_sspi_context((context_sspi)context);
281 g_free(context);
285 * @param message a NULL terminated string to sign
288 static sip_uint32
289 sip_sec_make_signature__sspi(SipSecContext context,
290 const char *message,
291 SipSecBuffer *signature)
293 SecBufferDesc buffs_desc;
294 SecBuffer buffs[2];
295 SECURITY_STATUS ret;
296 SecPkgContext_Sizes context_sizes;
297 unsigned char *signature_buff;
298 size_t signature_buff_length;
299 context_sspi ctx = (context_sspi) context;
301 ret = QueryContextAttributes(ctx->ctx_sspi,
302 SECPKG_ATTR_SIZES,
303 &context_sizes);
305 if (ret != SEC_E_OK) {
306 sip_sec_sspi_print_error("sip_sec_make_signature__sspi: QueryContextAttributes", ret);
307 return SIP_SEC_E_INTERNAL_ERROR;
310 signature_buff_length = context_sizes.cbMaxSignature;
311 signature_buff = g_malloc(signature_buff_length);
313 buffs_desc.cBuffers = 2;
314 buffs_desc.pBuffers = buffs;
315 buffs_desc.ulVersion = SECBUFFER_VERSION;
317 /* message to sign */
318 buffs[0].BufferType = SECBUFFER_DATA;
319 buffs[0].cbBuffer = strlen(message);
320 buffs[0].pvBuffer = (PVOID)message;
322 /* to hold signature */
323 buffs[1].BufferType = SECBUFFER_TOKEN;
324 buffs[1].cbBuffer = signature_buff_length;
325 buffs[1].pvBuffer = signature_buff;
327 ret = MakeSignature(ctx->ctx_sspi,
328 (ULONG)0,
329 &buffs_desc,
330 100);
331 if (ret != SEC_E_OK) {
332 sip_sec_sspi_print_error("sip_sec_make_signature__sspi: MakeSignature", ret);
333 g_free(signature_buff);
334 return SIP_SEC_E_INTERNAL_ERROR;
337 signature->value = signature_buff;
338 signature->length = buffs[1].cbBuffer;
340 return SIP_SEC_E_OK;
344 * @param message a NULL terminated string to check signature of
345 * @return SIP_SEC_E_OK on success
347 static sip_uint32
348 sip_sec_verify_signature__sspi(SipSecContext context,
349 const char *message,
350 SipSecBuffer signature)
352 SecBufferDesc buffs_desc;
353 SecBuffer buffs[2];
354 SECURITY_STATUS ret;
356 buffs_desc.cBuffers = 2;
357 buffs_desc.pBuffers = buffs;
358 buffs_desc.ulVersion = SECBUFFER_VERSION;
360 /* message to sign */
361 buffs[0].BufferType = SECBUFFER_DATA;
362 buffs[0].cbBuffer = strlen(message);
363 buffs[0].pvBuffer = (PVOID)message;
365 /* signature to check */
366 buffs[1].BufferType = SECBUFFER_TOKEN;
367 buffs[1].cbBuffer = signature.length;
368 buffs[1].pvBuffer = signature.value;
370 ret = VerifySignature(((context_sspi)context)->ctx_sspi,
371 &buffs_desc,
375 if (ret != SEC_E_OK) {
376 sip_sec_sspi_print_error("sip_sec_verify_signature__sspi: VerifySignature", ret);
377 return SIP_SEC_E_INTERNAL_ERROR;
380 return SIP_SEC_E_OK;
383 SipSecContext
384 sip_sec_create_context__sspi(guint type)
386 context_sspi context = g_malloc0(sizeof(struct _context_sspi));
387 if (!context) return(NULL);
389 context->common.acquire_cred_func = sip_sec_acquire_cred__sspi;
390 context->common.init_context_func = sip_sec_init_sec_context__sspi;
391 context->common.destroy_context_func = sip_sec_destroy_sec_context__sspi;
392 context->common.make_signature_func = sip_sec_make_signature__sspi;
393 context->common.verify_signature_func = sip_sec_verify_signature__sspi;
394 context->type = type;
395 context->initial = TRUE;
397 return((SipSecContext) context);
400 gboolean sip_sec_password__sspi(void)
402 /* SSPI supports Single-Sign On */
403 return(FALSE);
406 /* Utility Functions */
409 * Returns interval in seconds from now till provided value
411 static int
412 sip_sec_get_interval_from_now_sec(TimeStamp timestamp)
414 SYSTEMTIME stNow;
415 FILETIME ftNow;
416 ULARGE_INTEGER uliNow, uliTo;
418 GetLocalTime(&stNow);
419 SystemTimeToFileTime(&stNow, &ftNow);
421 uliNow.LowPart = ftNow.dwLowDateTime;
422 uliNow.HighPart = ftNow.dwHighDateTime;
424 uliTo.LowPart = timestamp.LowPart;
425 uliTo.HighPart = timestamp.HighPart;
427 return (int)((uliTo.QuadPart - uliNow.QuadPart)/10/1000/1000);
430 static void
431 sip_sec_sspi_print_error(const char *func,
432 SECURITY_STATUS ret)
434 char *error_message;
435 static char *buff;
436 int buff_length;
438 buff_length = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
439 FORMAT_MESSAGE_ALLOCATE_BUFFER |
440 FORMAT_MESSAGE_IGNORE_INSERTS,
442 ret,
444 (LPTSTR)&buff,
445 16384,
447 error_message = g_strndup(buff, buff_length);
448 LocalFree(buff);
450 SIPE_DEBUG_ERROR("SSPI ERROR [%d] in %s: %s", (int)ret, func, error_message);
451 g_free(error_message);
456 Local Variables:
457 mode: c
458 c-file-style: "bsd"
459 indent-tabs-mode: t
460 tab-width: 8
461 End: