ucs: add specification references
[siplcs.git] / src / core / sip-sec-sspi.c
blob8f6a2ac3d83b5cdf6ca7171980eba236c76ee0af
1 /**
2 * @file sip-sec-sspi.c
4 * pidgin-sipe
6 * Copyright (C) 2011-2014 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 */
56 "", /* SIPE_AUTHENTICATION_TYPE_AUTOMATIC */
59 #ifndef ISC_REQ_IDENTIFY
60 #define ISC_REQ_IDENTIFY 0x00002000
61 #endif
63 typedef struct _context_sspi {
64 struct sip_sec_context common;
65 CredHandle* cred_sspi;
66 CtxtHandle* ctx_sspi;
67 } *context_sspi;
69 #define SIP_SEC_FLAG_SSPI_SIP_NTLM 0x00010000
71 /* Utility Functions */
73 static void
74 sip_sec_sspi_print_error(const gchar *func,
75 SECURITY_STATUS ret)
77 gchar *error_message;
78 static char *buff;
79 guint buff_length;
81 buff_length = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
82 FORMAT_MESSAGE_ALLOCATE_BUFFER |
83 FORMAT_MESSAGE_IGNORE_INSERTS,
85 ret,
87 (LPTSTR)&buff,
88 16384,
89 0);
90 error_message = g_strndup(buff, buff_length);
91 LocalFree(buff);
93 SIPE_DEBUG_ERROR("SSPI ERROR [%d] in %s: %s", (int)ret, func, error_message);
94 g_free(error_message);
97 /* Returns interval in seconds from now till provided value */
98 static guint
99 sip_sec_get_interval_from_now_sec(TimeStamp timestamp)
101 SYSTEMTIME stNow;
102 FILETIME ftNow;
103 ULARGE_INTEGER uliNow, uliTo;
105 GetLocalTime(&stNow);
106 SystemTimeToFileTime(&stNow, &ftNow);
108 uliNow.LowPart = ftNow.dwLowDateTime;
109 uliNow.HighPart = ftNow.dwHighDateTime;
111 uliTo.LowPart = timestamp.LowPart;
112 uliTo.HighPart = timestamp.HighPart;
114 return((uliTo.QuadPart - uliNow.QuadPart)/10/1000/1000);
117 static void
118 sip_sec_destroy_sspi_context(context_sspi context)
120 if (context->ctx_sspi) {
121 DeleteSecurityContext(context->ctx_sspi);
122 g_free(context->ctx_sspi);
123 context->ctx_sspi = NULL;
125 if (context->cred_sspi) {
126 FreeCredentialsHandle(context->cred_sspi);
127 g_free(context->cred_sspi);
128 context->cred_sspi = NULL;
132 /* sip-sec-mech.h API implementation for SSPI - Kerberos, NTLM and Negotiate */
134 static gboolean
135 sip_sec_acquire_cred__sspi(SipSecContext context,
136 const gchar *domain,
137 const gchar *username,
138 const gchar *password)
140 SECURITY_STATUS ret;
141 TimeStamp expiry;
142 SEC_WINNT_AUTH_IDENTITY auth_identity;
143 context_sspi ctx = (context_sspi)context;
145 /* this is the first time we are allowed to set private flags */
146 if (((context->flags & SIP_SEC_FLAG_COMMON_HTTP) == 0) &&
147 (context->type == SIPE_AUTHENTICATION_TYPE_NTLM))
148 context->flags |= SIP_SEC_FLAG_SSPI_SIP_NTLM;
150 if ((context->flags & SIP_SEC_FLAG_COMMON_SSO) == 0) {
151 if (!username || !password) {
152 return FALSE;
155 memset(&auth_identity, 0, sizeof(auth_identity));
156 auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
158 if (!is_empty(domain)) {
159 auth_identity.Domain = (unsigned char*)domain;
160 auth_identity.DomainLength = strlen(domain);
163 auth_identity.User = (unsigned char*)username;
164 auth_identity.UserLength = strlen(username);
166 auth_identity.Password = (unsigned char*)password;
167 auth_identity.PasswordLength = strlen(password);
170 ctx->cred_sspi = g_malloc0(sizeof(CredHandle));
172 ret = AcquireCredentialsHandleA(NULL,
173 (SEC_CHAR *)mech_names[context->type],
174 SECPKG_CRED_OUTBOUND,
175 NULL,
176 (context->flags & SIP_SEC_FLAG_COMMON_SSO) ? NULL : &auth_identity,
177 NULL,
178 NULL,
179 ctx->cred_sspi,
180 &expiry);
182 if (ret != SEC_E_OK) {
183 sip_sec_sspi_print_error("sip_sec_acquire_cred__sspi: AcquireCredentialsHandleA", ret);
184 g_free(ctx->cred_sspi);
185 ctx->cred_sspi = NULL;
186 return FALSE;
187 } else {
188 return TRUE;
192 static gboolean
193 sip_sec_init_sec_context__sspi(SipSecContext context,
194 SipSecBuffer in_buff,
195 SipSecBuffer *out_buff,
196 const gchar *service_name)
198 TimeStamp expiry;
199 SecBufferDesc input_desc, output_desc;
200 SecBuffer in_token, out_token;
201 SECURITY_STATUS ret;
202 ULONG req_flags;
203 ULONG ret_flags;
204 context_sspi ctx = (context_sspi)context;
205 CtxtHandle* out_context;
207 SIPE_DEBUG_INFO_NOFORMAT("sip_sec_init_sec_context__sspi: in use");
210 * If authentication was already completed, then this mean a new
211 * authentication handshake has started on the existing connection.
212 * We must throw away the old context, because we need a new one.
214 if ((context->flags & SIP_SEC_FLAG_COMMON_READY) &&
215 ctx->ctx_sspi) {
216 SIPE_DEBUG_INFO_NOFORMAT("sip_sec_init_sec_context__sspi: dropping old context");
217 DeleteSecurityContext(ctx->ctx_sspi);
218 g_free(ctx->ctx_sspi);
219 ctx->ctx_sspi = NULL;
220 context->flags &= ~SIP_SEC_FLAG_COMMON_READY;
223 /* reuse existing context on following calls */
224 out_context = ctx->ctx_sspi ? ctx->ctx_sspi : g_malloc0(sizeof(CtxtHandle));
226 input_desc.cBuffers = 1;
227 input_desc.pBuffers = &in_token;
228 input_desc.ulVersion = SECBUFFER_VERSION;
230 /* input token */
231 in_token.BufferType = SECBUFFER_TOKEN;
232 in_token.cbBuffer = in_buff.length;
233 in_token.pvBuffer = in_buff.value;
235 output_desc.cBuffers = 1;
236 output_desc.pBuffers = &out_token;
237 output_desc.ulVersion = SECBUFFER_VERSION;
239 /* to hold output token */
240 out_token.BufferType = SECBUFFER_TOKEN;
241 out_token.cbBuffer = 0;
242 out_token.pvBuffer = NULL;
244 req_flags = (ISC_REQ_ALLOCATE_MEMORY |
245 ISC_REQ_INTEGRITY |
246 ISC_REQ_IDENTIFY);
248 if (context->flags & SIP_SEC_FLAG_SSPI_SIP_NTLM) {
249 req_flags |= (ISC_REQ_DATAGRAM);
252 ret = InitializeSecurityContextA(ctx->cred_sspi,
253 ctx->ctx_sspi,
254 (SEC_CHAR *)service_name,
255 req_flags,
257 SECURITY_NATIVE_DREP,
258 &input_desc,
260 out_context,
261 &output_desc,
262 &ret_flags,
263 &expiry);
265 if (ret != SEC_E_OK && ret != SEC_I_CONTINUE_NEEDED) {
266 if (!ctx->ctx_sspi)
267 g_free(out_context);
268 sip_sec_destroy_sspi_context(ctx);
269 sip_sec_sspi_print_error("sip_sec_init_sec_context__sspi: InitializeSecurityContextA", ret);
270 return FALSE;
273 out_buff->length = out_token.cbBuffer;
274 if (out_token.cbBuffer) {
275 out_buff->value = g_malloc(out_token.cbBuffer);
276 memcpy(out_buff->value, out_token.pvBuffer, out_token.cbBuffer);
277 } else {
278 /* Special case: empty token */
279 out_buff->value = (guint8 *) g_strdup("");
281 FreeContextBuffer(out_token.pvBuffer);
283 ctx->ctx_sspi = out_context;
285 if (context->type == SIPE_AUTHENTICATION_TYPE_KERBEROS) {
286 context->expires = sip_sec_get_interval_from_now_sec(expiry);
289 if (ret != SEC_I_CONTINUE_NEEDED) {
290 /* Authentication is completed */
291 context->flags |= SIP_SEC_FLAG_COMMON_READY;
294 return TRUE;
297 static void
298 sip_sec_destroy_sec_context__sspi(SipSecContext context)
300 sip_sec_destroy_sspi_context((context_sspi)context);
301 g_free(context);
305 * @param message a NULL terminated string to sign
308 static gboolean
309 sip_sec_make_signature__sspi(SipSecContext context,
310 const gchar *message,
311 SipSecBuffer *signature)
313 SecBufferDesc buffs_desc;
314 SecBuffer buffs[2];
315 SECURITY_STATUS ret;
316 SecPkgContext_Sizes context_sizes;
317 guchar *signature_buff;
318 size_t signature_buff_length;
319 context_sspi ctx = (context_sspi) context;
321 ret = QueryContextAttributes(ctx->ctx_sspi,
322 SECPKG_ATTR_SIZES,
323 &context_sizes);
325 if (ret != SEC_E_OK) {
326 sip_sec_sspi_print_error("sip_sec_make_signature__sspi: QueryContextAttributes", ret);
327 return FALSE;
330 signature_buff_length = context_sizes.cbMaxSignature;
331 signature_buff = g_malloc(signature_buff_length);
333 buffs_desc.cBuffers = 2;
334 buffs_desc.pBuffers = buffs;
335 buffs_desc.ulVersion = SECBUFFER_VERSION;
337 /* message to sign */
338 buffs[0].BufferType = SECBUFFER_DATA;
339 buffs[0].cbBuffer = strlen(message);
340 buffs[0].pvBuffer = (PVOID)message;
342 /* to hold signature */
343 buffs[1].BufferType = SECBUFFER_TOKEN;
344 buffs[1].cbBuffer = signature_buff_length;
345 buffs[1].pvBuffer = signature_buff;
347 ret = MakeSignature(ctx->ctx_sspi,
348 (ULONG)0,
349 &buffs_desc,
350 100);
351 if (ret != SEC_E_OK) {
352 sip_sec_sspi_print_error("sip_sec_make_signature__sspi: MakeSignature", ret);
353 g_free(signature_buff);
354 return FALSE;
357 signature->value = signature_buff;
358 signature->length = buffs[1].cbBuffer;
360 return TRUE;
364 * @param message a NULL terminated string to check signature of
365 * @return TRUE on success
367 static gboolean
368 sip_sec_verify_signature__sspi(SipSecContext context,
369 const gchar *message,
370 SipSecBuffer signature)
372 SecBufferDesc buffs_desc;
373 SecBuffer buffs[2];
374 SECURITY_STATUS ret;
376 buffs_desc.cBuffers = 2;
377 buffs_desc.pBuffers = buffs;
378 buffs_desc.ulVersion = SECBUFFER_VERSION;
380 /* message to sign */
381 buffs[0].BufferType = SECBUFFER_DATA;
382 buffs[0].cbBuffer = strlen(message);
383 buffs[0].pvBuffer = (PVOID)message;
385 /* signature to check */
386 buffs[1].BufferType = SECBUFFER_TOKEN;
387 buffs[1].cbBuffer = signature.length;
388 buffs[1].pvBuffer = signature.value;
390 ret = VerifySignature(((context_sspi)context)->ctx_sspi,
391 &buffs_desc,
395 if (ret != SEC_E_OK) {
396 sip_sec_sspi_print_error("sip_sec_verify_signature__sspi: VerifySignature", ret);
397 return FALSE;
400 return TRUE;
403 /* SSPI implements SPNEGO (RFC 4559) */
404 static const gchar *
405 sip_sec_context_name__sspi(SipSecContext context)
407 return(mech_names[context->type]);
410 SipSecContext
411 sip_sec_create_context__sspi(SIPE_UNUSED_PARAMETER guint type)
413 context_sspi context = g_malloc0(sizeof(struct _context_sspi));
414 if (!context) return(NULL);
416 context->common.acquire_cred_func = sip_sec_acquire_cred__sspi;
417 context->common.init_context_func = sip_sec_init_sec_context__sspi;
418 context->common.destroy_context_func = sip_sec_destroy_sec_context__sspi;
419 context->common.make_signature_func = sip_sec_make_signature__sspi;
420 context->common.verify_signature_func = sip_sec_verify_signature__sspi;
421 context->common.context_name_func = sip_sec_context_name__sspi;
423 return((SipSecContext) context);
426 gboolean sip_sec_password__sspi(void)
428 /* SSPI supports Single-Sign On */
429 return(FALSE);
433 Local Variables:
434 mode: c
435 c-file-style: "bsd"
436 indent-tabs-mode: t
437 tab-width: 8
438 End: