security: minor code reshuffle in SSPI
[siplcs.git] / src / core / sip-sec-sspi.c
blobd42cd239a8e26799c9c040ea7f6bdd477ce579e7
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_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 (ctx->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[ctx->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;
221 /* reuse existing context on following calls */
222 out_context = ctx->ctx_sspi ? ctx->ctx_sspi : g_malloc0(sizeof(CtxtHandle));
224 input_desc.cBuffers = 1;
225 input_desc.pBuffers = &in_token;
226 input_desc.ulVersion = SECBUFFER_VERSION;
228 /* input token */
229 in_token.BufferType = SECBUFFER_TOKEN;
230 in_token.cbBuffer = in_buff.length;
231 in_token.pvBuffer = in_buff.value;
233 output_desc.cBuffers = 1;
234 output_desc.pBuffers = &out_token;
235 output_desc.ulVersion = SECBUFFER_VERSION;
237 /* to hold output token */
238 out_token.BufferType = SECBUFFER_TOKEN;
239 out_token.cbBuffer = 0;
240 out_token.pvBuffer = NULL;
242 req_flags = (ISC_REQ_ALLOCATE_MEMORY |
243 ISC_REQ_INTEGRITY |
244 ISC_REQ_IDENTIFY);
246 if (context->flags & SIP_SEC_FLAG_SSPI_SIP_NTLM) {
247 req_flags |= (ISC_REQ_DATAGRAM);
250 ret = InitializeSecurityContextA(ctx->cred_sspi,
251 ctx->ctx_sspi,
252 (SEC_CHAR *)service_name,
253 req_flags,
255 SECURITY_NATIVE_DREP,
256 &input_desc,
258 out_context,
259 &output_desc,
260 &ret_flags,
261 &expiry);
263 if (ret != SEC_E_OK && ret != SEC_I_CONTINUE_NEEDED) {
264 if (!ctx->ctx_sspi)
265 g_free(out_context);
266 sip_sec_destroy_sspi_context(ctx);
267 sip_sec_sspi_print_error("sip_sec_init_sec_context__sspi: InitializeSecurityContextA", ret);
268 return FALSE;
271 out_buff->length = out_token.cbBuffer;
272 if (out_token.cbBuffer) {
273 out_buff->value = g_malloc(out_token.cbBuffer);
274 memcpy(out_buff->value, out_token.pvBuffer, out_token.cbBuffer);
275 } else {
276 /* Special case: empty token */
277 out_buff->value = (guint8 *) g_strdup("");
279 FreeContextBuffer(out_token.pvBuffer);
281 ctx->ctx_sspi = out_context;
283 if (ctx->type == SIPE_AUTHENTICATION_TYPE_KERBEROS) {
284 context->expires = sip_sec_get_interval_from_now_sec(expiry);
287 if (ret != SEC_I_CONTINUE_NEEDED) {
288 /* Authentication is completed */
289 context->flags |= SIP_SEC_FLAG_COMMON_READY;
292 return TRUE;
295 static void
296 sip_sec_destroy_sec_context__sspi(SipSecContext context)
298 sip_sec_destroy_sspi_context((context_sspi)context);
299 g_free(context);
303 * @param message a NULL terminated string to sign
306 static gboolean
307 sip_sec_make_signature__sspi(SipSecContext context,
308 const gchar *message,
309 SipSecBuffer *signature)
311 SecBufferDesc buffs_desc;
312 SecBuffer buffs[2];
313 SECURITY_STATUS ret;
314 SecPkgContext_Sizes context_sizes;
315 guchar *signature_buff;
316 size_t signature_buff_length;
317 context_sspi ctx = (context_sspi) context;
319 ret = QueryContextAttributes(ctx->ctx_sspi,
320 SECPKG_ATTR_SIZES,
321 &context_sizes);
323 if (ret != SEC_E_OK) {
324 sip_sec_sspi_print_error("sip_sec_make_signature__sspi: QueryContextAttributes", ret);
325 return FALSE;
328 signature_buff_length = context_sizes.cbMaxSignature;
329 signature_buff = g_malloc(signature_buff_length);
331 buffs_desc.cBuffers = 2;
332 buffs_desc.pBuffers = buffs;
333 buffs_desc.ulVersion = SECBUFFER_VERSION;
335 /* message to sign */
336 buffs[0].BufferType = SECBUFFER_DATA;
337 buffs[0].cbBuffer = strlen(message);
338 buffs[0].pvBuffer = (PVOID)message;
340 /* to hold signature */
341 buffs[1].BufferType = SECBUFFER_TOKEN;
342 buffs[1].cbBuffer = signature_buff_length;
343 buffs[1].pvBuffer = signature_buff;
345 ret = MakeSignature(ctx->ctx_sspi,
346 (ULONG)0,
347 &buffs_desc,
348 100);
349 if (ret != SEC_E_OK) {
350 sip_sec_sspi_print_error("sip_sec_make_signature__sspi: MakeSignature", ret);
351 g_free(signature_buff);
352 return FALSE;
355 signature->value = signature_buff;
356 signature->length = buffs[1].cbBuffer;
358 return TRUE;
362 * @param message a NULL terminated string to check signature of
363 * @return TRUE on success
365 static gboolean
366 sip_sec_verify_signature__sspi(SipSecContext context,
367 const gchar *message,
368 SipSecBuffer signature)
370 SecBufferDesc buffs_desc;
371 SecBuffer buffs[2];
372 SECURITY_STATUS ret;
374 buffs_desc.cBuffers = 2;
375 buffs_desc.pBuffers = buffs;
376 buffs_desc.ulVersion = SECBUFFER_VERSION;
378 /* message to sign */
379 buffs[0].BufferType = SECBUFFER_DATA;
380 buffs[0].cbBuffer = strlen(message);
381 buffs[0].pvBuffer = (PVOID)message;
383 /* signature to check */
384 buffs[1].BufferType = SECBUFFER_TOKEN;
385 buffs[1].cbBuffer = signature.length;
386 buffs[1].pvBuffer = signature.value;
388 ret = VerifySignature(((context_sspi)context)->ctx_sspi,
389 &buffs_desc,
393 if (ret != SEC_E_OK) {
394 sip_sec_sspi_print_error("sip_sec_verify_signature__sspi: VerifySignature", ret);
395 return FALSE;
398 return TRUE;
401 SipSecContext
402 sip_sec_create_context__sspi(guint type)
404 context_sspi context = g_malloc0(sizeof(struct _context_sspi));
405 if (!context) return(NULL);
407 context->common.acquire_cred_func = sip_sec_acquire_cred__sspi;
408 context->common.init_context_func = sip_sec_init_sec_context__sspi;
409 context->common.destroy_context_func = sip_sec_destroy_sec_context__sspi;
410 context->common.make_signature_func = sip_sec_make_signature__sspi;
411 context->common.verify_signature_func = sip_sec_verify_signature__sspi;
412 context->type = type;
414 return((SipSecContext) context);
417 gboolean sip_sec_password__sspi(void)
419 /* SSPI supports Single-Sign On */
420 return(FALSE);
424 Local Variables:
425 mode: c
426 c-file-style: "bsd"
427 indent-tabs-mode: t
428 tab-width: 8
429 End: