sspi: fix memory leak
[siplcs.git] / src / core / sip-sec-sspi.c
blobabb54979ae80e31d3565f70b4ddfa5d47c67534b
1 /**
2 * @file sip-sec-sspi.c
4 * pidgin-sipe
6 * Copyright (C) 2011-12 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 <stdio.h>
26 #include <windows.h>
27 #include <rpc.h>
28 #ifdef HAVE_CONFIG_H
29 /* This should not affect Makefile.mingw builds, as it doesn't use config.h */
30 #ifndef SECURITY_WIN32
31 #define SECURITY_WIN32 1
32 #endif
33 #endif
34 #include <security.h>
36 #include <glib.h>
38 #include "sip-sec.h"
39 #include "sip-sec-mech.h"
40 #include "sip-sec-sspi.h"
41 #include "sipe-backend.h"
43 /* Mechanism names */
44 #define SSPI_MECH_NTLM "NTLM"
45 #define SSPI_MECH_KERBEROS "Kerberos"
46 #define SSPI_MECH_NEGOTIATE "Negotiate"
47 #define SSPI_MECH_TLS_DSK "Schannel" /* SSL/TLS provider, is this correct? */
49 #ifndef ISC_REQ_IDENTIFY
50 #define ISC_REQ_IDENTIFY 0x00002000
51 #endif
53 typedef struct _context_sspi {
54 struct sip_sec_context common;
55 CredHandle* cred_sspi;
56 CtxtHandle* ctx_sspi;
57 /** Kerberos or NTLM */
58 const char *mech;
59 } *context_sspi;
61 static int
62 sip_sec_get_interval_from_now_sec(TimeStamp timestamp);
64 void
65 sip_sec_sspi_print_error(const char *func,
66 SECURITY_STATUS ret);
68 /** internal method */
69 static void
70 sip_sec_destroy_sspi_context(context_sspi context)
72 if (context->ctx_sspi)
73 DeleteSecurityContext(context->ctx_sspi);
74 if (context->cred_sspi)
75 FreeCredentialsHandle(context->cred_sspi);
78 /* sip-sec-mech.h API implementation for SSPI - Kerberos and NTLM */
80 static sip_uint32
81 sip_sec_acquire_cred__sspi(SipSecContext context,
82 const char *domain,
83 const char *username,
84 const char *password)
86 SECURITY_STATUS ret;
87 TimeStamp expiry;
88 SEC_WINNT_AUTH_IDENTITY auth_identity;
89 context_sspi ctx = (context_sspi)context;
91 if (username) {
92 if (!password) {
93 return SIP_SEC_E_INTERNAL_ERROR;
96 memset(&auth_identity, 0, sizeof(auth_identity));
97 auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
99 if ( domain && (strlen(domain) > 0) ) {
100 auth_identity.Domain = (unsigned char*)domain;
101 auth_identity.DomainLength = strlen(domain);
104 auth_identity.User = (unsigned char*)username;
105 auth_identity.UserLength = strlen(username);
107 auth_identity.Password = (unsigned char*)password;
108 auth_identity.PasswordLength = strlen(password);
111 ctx->cred_sspi = g_malloc0(sizeof(CredHandle));
113 /* @TODO: this does not work for "Schannel" (TLS-DSK) as it expects
114 a SCHANNEL_CRED datastructure, pointing to the private key
115 and the client certificate */
116 ret = AcquireCredentialsHandleA(NULL,
117 (SEC_CHAR *)ctx->mech,
118 SECPKG_CRED_OUTBOUND,
119 NULL,
120 (context->sso || !username) ? NULL : &auth_identity,
121 NULL,
122 NULL,
123 ctx->cred_sspi,
124 &expiry);
126 if (ret != SEC_E_OK) {
127 sip_sec_sspi_print_error("sip_sec_acquire_cred__sspi: AcquireCredentialsHandleA", ret);
128 g_free(ctx->cred_sspi);
129 ctx->cred_sspi = NULL;
130 return SIP_SEC_E_INTERNAL_ERROR;
131 } else {
132 return SIP_SEC_E_OK;
136 static sip_uint32
137 sip_sec_init_sec_context__sspi(SipSecContext context,
138 SipSecBuffer in_buff,
139 SipSecBuffer *out_buff,
140 const char *service_name)
142 TimeStamp expiry;
143 SecBufferDesc input_desc, output_desc;
144 SecBuffer in_token, out_token;
145 SECURITY_STATUS ret;
146 ULONG req_flags;
147 ULONG ret_flags;
148 context_sspi ctx = (context_sspi)context;
149 CtxtHandle* out_context = g_malloc0(sizeof(CtxtHandle));
151 SIPE_DEBUG_INFO_NOFORMAT("sip_sec_init_sec_context__sspi: in use");
153 input_desc.cBuffers = 1;
154 input_desc.pBuffers = &in_token;
155 input_desc.ulVersion = SECBUFFER_VERSION;
157 /* input token */
158 in_token.BufferType = SECBUFFER_TOKEN;
159 in_token.cbBuffer = in_buff.length;
160 in_token.pvBuffer = in_buff.value;
162 output_desc.cBuffers = 1;
163 output_desc.pBuffers = &out_token;
164 output_desc.ulVersion = SECBUFFER_VERSION;
166 /* to hold output token */
167 out_token.BufferType = SECBUFFER_TOKEN;
168 out_token.cbBuffer = 0;
169 out_token.pvBuffer = NULL;
171 req_flags = (ISC_REQ_ALLOCATE_MEMORY |
172 ISC_REQ_INTEGRITY |
173 ISC_REQ_IDENTIFY);
175 if (ctx->mech && !strcmp(ctx->mech, SSPI_MECH_NTLM) &&
176 !context->is_connection_based)
178 req_flags |= (ISC_REQ_DATAGRAM);
181 ret = InitializeSecurityContextA(ctx->cred_sspi,
182 ctx->ctx_sspi,
183 (SEC_CHAR *)service_name,
184 req_flags,
186 SECURITY_NATIVE_DREP,
187 &input_desc,
189 out_context,
190 &output_desc,
191 &ret_flags,
192 &expiry);
194 if (ret != SEC_E_OK && ret != SEC_I_CONTINUE_NEEDED) {
195 sip_sec_destroy_sspi_context(ctx);
196 sip_sec_sspi_print_error("sip_sec_init_sec_context__sspi: InitializeSecurityContextA", ret);
197 return SIP_SEC_E_INTERNAL_ERROR;
200 out_buff->length = out_token.cbBuffer;
201 out_buff->value = NULL;
202 if (out_token.cbBuffer) {
203 out_buff->value = g_malloc0(out_token.cbBuffer);
204 memmove(out_buff->value, out_token.pvBuffer, out_token.cbBuffer);
205 FreeContextBuffer(out_token.pvBuffer);
208 ctx->ctx_sspi = out_context;
209 if (ctx->mech && !strcmp(ctx->mech, SSPI_MECH_KERBEROS)) {
210 context->expires = sip_sec_get_interval_from_now_sec(expiry);
213 if (ret == SEC_I_CONTINUE_NEEDED) {
214 return SIP_SEC_I_CONTINUE_NEEDED;
215 } else {
216 return SIP_SEC_E_OK;
220 static void
221 sip_sec_destroy_sec_context__sspi(SipSecContext context)
223 sip_sec_destroy_sspi_context((context_sspi)context);
224 g_free(context);
228 * @param message a NULL terminated string to sign
231 static sip_uint32
232 sip_sec_make_signature__sspi(SipSecContext context,
233 const char *message,
234 SipSecBuffer *signature)
236 SecBufferDesc buffs_desc;
237 SecBuffer buffs[2];
238 SECURITY_STATUS ret;
239 SecPkgContext_Sizes context_sizes;
240 unsigned char *signature_buff;
241 size_t signature_buff_length;
242 context_sspi ctx = (context_sspi) context;
244 ret = QueryContextAttributes(ctx->ctx_sspi,
245 SECPKG_ATTR_SIZES,
246 &context_sizes);
248 if (ret != SEC_E_OK) {
249 sip_sec_sspi_print_error("sip_sec_make_signature__sspi: QueryContextAttributes", ret);
250 return SIP_SEC_E_INTERNAL_ERROR;
253 signature_buff_length = context_sizes.cbMaxSignature;
254 signature_buff = g_malloc0(signature_buff_length);
256 buffs_desc.cBuffers = 2;
257 buffs_desc.pBuffers = buffs;
258 buffs_desc.ulVersion = SECBUFFER_VERSION;
260 /* message to sign */
261 buffs[0].BufferType = SECBUFFER_DATA;
262 buffs[0].cbBuffer = strlen(message);
263 buffs[0].pvBuffer = (PVOID)message;
265 /* to hold signature */
266 buffs[1].BufferType = SECBUFFER_TOKEN;
267 buffs[1].cbBuffer = signature_buff_length;
268 buffs[1].pvBuffer = signature_buff;
270 ret = MakeSignature(ctx->ctx_sspi,
271 (ULONG)0,
272 &buffs_desc,
273 100);
274 if (ret != SEC_E_OK) {
275 sip_sec_sspi_print_error("sip_sec_make_signature__sspi: MakeSignature", ret);
276 g_free(signature_buff);
277 return SIP_SEC_E_INTERNAL_ERROR;
280 signature->value = signature_buff;
281 signature->length = buffs[1].cbBuffer;
283 return SIP_SEC_E_OK;
287 * @param message a NULL terminated string to check signature of
288 * @return SIP_SEC_E_OK on success
290 static sip_uint32
291 sip_sec_verify_signature__sspi(SipSecContext context,
292 const char *message,
293 SipSecBuffer signature)
295 SecBufferDesc buffs_desc;
296 SecBuffer buffs[2];
297 SECURITY_STATUS ret;
299 buffs_desc.cBuffers = 2;
300 buffs_desc.pBuffers = buffs;
301 buffs_desc.ulVersion = SECBUFFER_VERSION;
303 /* message to sign */
304 buffs[0].BufferType = SECBUFFER_DATA;
305 buffs[0].cbBuffer = strlen(message);
306 buffs[0].pvBuffer = (PVOID)message;
308 /* signature to check */
309 buffs[1].BufferType = SECBUFFER_TOKEN;
310 buffs[1].cbBuffer = signature.length;
311 buffs[1].pvBuffer = signature.value;
313 ret = VerifySignature(((context_sspi)context)->ctx_sspi,
314 &buffs_desc,
318 if (ret != SEC_E_OK) {
319 sip_sec_sspi_print_error("sip_sec_verify_signature__sspi: VerifySignature", ret);
320 return SIP_SEC_E_INTERNAL_ERROR;
323 return SIP_SEC_E_OK;
326 SipSecContext
327 sip_sec_create_context__sspi(guint type)
329 context_sspi context = g_malloc0(sizeof(struct _context_sspi));
330 if (!context) return(NULL);
332 context->common.acquire_cred_func = sip_sec_acquire_cred__sspi;
333 context->common.init_context_func = sip_sec_init_sec_context__sspi;
334 context->common.destroy_context_func = sip_sec_destroy_sec_context__sspi;
335 context->common.make_signature_func = sip_sec_make_signature__sspi;
336 context->common.verify_signature_func = sip_sec_verify_signature__sspi;
337 context->mech = (type == AUTH_TYPE_NTLM) ? SSPI_MECH_NTLM :
338 ((type == AUTH_TYPE_KERBEROS) ? SSPI_MECH_KERBEROS :
339 ((type == AUTH_TYPE_NEGOTIATE) ? SSPI_MECH_NEGOTIATE : SSPI_MECH_TLS_DSK));
341 return((SipSecContext) context);
344 /* Utility Functions */
347 * Returns interval in seconds from now till provided value
349 static int
350 sip_sec_get_interval_from_now_sec(TimeStamp timestamp)
352 SYSTEMTIME stNow;
353 FILETIME ftNow;
354 ULARGE_INTEGER uliNow, uliTo;
356 GetLocalTime(&stNow);
357 SystemTimeToFileTime(&stNow, &ftNow);
359 uliNow.LowPart = ftNow.dwLowDateTime;
360 uliNow.HighPart = ftNow.dwHighDateTime;
362 uliTo.LowPart = timestamp.LowPart;
363 uliTo.HighPart = timestamp.HighPart;
365 return (int)((uliTo.QuadPart - uliNow.QuadPart)/10/1000/1000);
368 void
369 sip_sec_sspi_print_error(const char *func,
370 SECURITY_STATUS ret)
372 char *error_message;
373 static char *buff;
374 int buff_length;
376 buff_length = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
377 FORMAT_MESSAGE_ALLOCATE_BUFFER |
378 FORMAT_MESSAGE_IGNORE_INSERTS,
380 ret,
382 (LPTSTR)&buff,
383 16384,
385 error_message = g_strndup(buff, buff_length);
386 LocalFree(buff);
388 printf("SSPI ERROR [%d] in %s: %s", (int)ret, func, error_message);
389 g_free(error_message);
394 Local Variables:
395 mode: c
396 c-file-style: "bsd"
397 indent-tabs-mode: t
398 tab-width: 8
399 End: