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
26 #error sip-sec-sspi.c can only be compiled for Windows builds
31 #ifndef SECURITY_WIN32
32 #define SECURITY_WIN32 1
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"
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
61 typedef struct _context_sspi
{
62 struct sip_sec_context common
;
63 CredHandle
* cred_sspi
;
68 #define SIP_SEC_FLAG_SSPI_INITIAL 0x00010000
69 #define SIP_SEC_FLAG_SSPI_SIP_NTLM 0x00020000
72 sip_sec_get_interval_from_now_sec(TimeStamp timestamp
);
75 sip_sec_sspi_print_error(const gchar
*func
,
78 /** internal method */
80 sip_sec_destroy_sspi_context(context_sspi context
)
82 if (context
->ctx_sspi
) {
83 DeleteSecurityContext(context
->ctx_sspi
);
84 g_free(context
->ctx_sspi
);
85 context
->ctx_sspi
= NULL
;
87 if (context
->cred_sspi
) {
88 FreeCredentialsHandle(context
->cred_sspi
);
89 g_free(context
->cred_sspi
);
90 context
->cred_sspi
= NULL
;
94 /* sip-sec-mech.h API implementation for SSPI - Kerberos and NTLM */
97 sip_sec_acquire_cred__sspi(SipSecContext context
,
99 const gchar
*username
,
100 const gchar
*password
)
104 SEC_WINNT_AUTH_IDENTITY auth_identity
;
105 context_sspi ctx
= (context_sspi
)context
;
107 /* this is the first time we are allowed to set private flags */
108 context
->flags
|= SIP_SEC_FLAG_SSPI_INITIAL
;
110 if (((context
->flags
& SIP_SEC_FLAG_COMMON_HTTP
) == 0) &&
111 (ctx
->type
== SIPE_AUTHENTICATION_TYPE_NTLM
))
112 context
->flags
|= SIP_SEC_FLAG_SSPI_SIP_NTLM
;
114 if ((context
->flags
& SIP_SEC_FLAG_COMMON_SSO
) == 0) {
115 if (!username
|| !password
) {
119 memset(&auth_identity
, 0, sizeof(auth_identity
));
120 auth_identity
.Flags
= SEC_WINNT_AUTH_IDENTITY_ANSI
;
122 if (!is_empty(domain
)) {
123 auth_identity
.Domain
= (unsigned char*)domain
;
124 auth_identity
.DomainLength
= strlen(domain
);
127 auth_identity
.User
= (unsigned char*)username
;
128 auth_identity
.UserLength
= strlen(username
);
130 auth_identity
.Password
= (unsigned char*)password
;
131 auth_identity
.PasswordLength
= strlen(password
);
134 ctx
->cred_sspi
= g_malloc0(sizeof(CredHandle
));
136 ret
= AcquireCredentialsHandleA(NULL
,
137 (SEC_CHAR
*)mech_names
[ctx
->type
],
138 SECPKG_CRED_OUTBOUND
,
140 (context
->flags
& SIP_SEC_FLAG_COMMON_SSO
) ? NULL
: &auth_identity
,
146 if (ret
!= SEC_E_OK
) {
147 sip_sec_sspi_print_error("sip_sec_acquire_cred__sspi: AcquireCredentialsHandleA", ret
);
148 g_free(ctx
->cred_sspi
);
149 ctx
->cred_sspi
= NULL
;
157 sip_sec_init_sec_context__sspi(SipSecContext context
,
158 SipSecBuffer in_buff
,
159 SipSecBuffer
*out_buff
,
160 const gchar
*service_name
)
163 SecBufferDesc input_desc
, output_desc
;
164 SecBuffer in_token
, out_token
;
168 context_sspi ctx
= (context_sspi
)context
;
169 CtxtHandle
* out_context
;
171 SIPE_DEBUG_INFO_NOFORMAT("sip_sec_init_sec_context__sspi: in use");
173 if (context
->flags
& SIP_SEC_FLAG_SSPI_SIP_NTLM
) {
174 if (context
->flags
& SIP_SEC_FLAG_SSPI_INITIAL
) {
175 /* empty initial message for connection-less NTLM */
176 if (in_buff
.value
== NULL
) {
177 SIPE_DEBUG_INFO_NOFORMAT("sip_sec_init_sec_context__sspi: initial message for connection-less NTLM");
178 out_buff
->length
= 0;
179 out_buff
->value
= (guint8
*) g_strdup("");
182 /* call again to create context for connection-less NTLM */
184 SipSecBuffer empty
= { 0, NULL
};
186 context
->flags
&= ~SIP_SEC_FLAG_SSPI_INITIAL
;
187 if (sip_sec_init_sec_context__sspi(context
,
191 SIPE_DEBUG_INFO_NOFORMAT("sip_sec_init_sec_context__sspi: connection-less NTLM second round");
192 g_free(out_buff
->value
);
198 } else if ((context
->flags
& SIP_SEC_FLAG_COMMON_HTTP
) &&
200 (in_buff
.value
== NULL
)) {
202 * We already have an initialized connection-based context
203 * and we're asked to initialize it with a NULL token. This
204 * will fail with "invalid token". Drop old context instead.
206 SIPE_DEBUG_INFO_NOFORMAT("sip_sec_init_sec_context__sspi: dropping old context");
207 DeleteSecurityContext(ctx
->ctx_sspi
);
208 g_free(ctx
->ctx_sspi
);
209 ctx
->ctx_sspi
= NULL
;
212 /* reuse existing context on following calls */
213 out_context
= ctx
->ctx_sspi
? ctx
->ctx_sspi
: g_malloc0(sizeof(CtxtHandle
));
215 input_desc
.cBuffers
= 1;
216 input_desc
.pBuffers
= &in_token
;
217 input_desc
.ulVersion
= SECBUFFER_VERSION
;
220 in_token
.BufferType
= SECBUFFER_TOKEN
;
221 in_token
.cbBuffer
= in_buff
.length
;
222 in_token
.pvBuffer
= in_buff
.value
;
224 output_desc
.cBuffers
= 1;
225 output_desc
.pBuffers
= &out_token
;
226 output_desc
.ulVersion
= SECBUFFER_VERSION
;
228 /* to hold output token */
229 out_token
.BufferType
= SECBUFFER_TOKEN
;
230 out_token
.cbBuffer
= 0;
231 out_token
.pvBuffer
= NULL
;
233 req_flags
= (ISC_REQ_ALLOCATE_MEMORY
|
237 if (context
->flags
& SIP_SEC_FLAG_SSPI_SIP_NTLM
) {
238 req_flags
|= (ISC_REQ_DATAGRAM
);
241 ret
= InitializeSecurityContextA(ctx
->cred_sspi
,
243 (SEC_CHAR
*)service_name
,
246 SECURITY_NATIVE_DREP
,
254 if (ret
!= SEC_E_OK
&& ret
!= SEC_I_CONTINUE_NEEDED
) {
257 sip_sec_destroy_sspi_context(ctx
);
258 sip_sec_sspi_print_error("sip_sec_init_sec_context__sspi: InitializeSecurityContextA", ret
);
262 out_buff
->length
= out_token
.cbBuffer
;
263 out_buff
->value
= NULL
;
264 if (out_token
.cbBuffer
) {
265 out_buff
->value
= g_malloc(out_token
.cbBuffer
);
266 memcpy(out_buff
->value
, out_token
.pvBuffer
, out_token
.cbBuffer
);
267 FreeContextBuffer(out_token
.pvBuffer
);
270 ctx
->ctx_sspi
= out_context
;
272 if (ctx
->type
== SIPE_AUTHENTICATION_TYPE_KERBEROS
) {
273 context
->expires
= sip_sec_get_interval_from_now_sec(expiry
);
276 if (ret
!= SEC_I_CONTINUE_NEEDED
) {
277 /* Authentication is completed */
278 context
->flags
|= SIP_SEC_FLAG_COMMON_READY
;
285 sip_sec_destroy_sec_context__sspi(SipSecContext context
)
287 sip_sec_destroy_sspi_context((context_sspi
)context
);
292 * @param message a NULL terminated string to sign
296 sip_sec_make_signature__sspi(SipSecContext context
,
297 const gchar
*message
,
298 SipSecBuffer
*signature
)
300 SecBufferDesc buffs_desc
;
303 SecPkgContext_Sizes context_sizes
;
304 guchar
*signature_buff
;
305 size_t signature_buff_length
;
306 context_sspi ctx
= (context_sspi
) context
;
308 ret
= QueryContextAttributes(ctx
->ctx_sspi
,
312 if (ret
!= SEC_E_OK
) {
313 sip_sec_sspi_print_error("sip_sec_make_signature__sspi: QueryContextAttributes", ret
);
317 signature_buff_length
= context_sizes
.cbMaxSignature
;
318 signature_buff
= g_malloc(signature_buff_length
);
320 buffs_desc
.cBuffers
= 2;
321 buffs_desc
.pBuffers
= buffs
;
322 buffs_desc
.ulVersion
= SECBUFFER_VERSION
;
324 /* message to sign */
325 buffs
[0].BufferType
= SECBUFFER_DATA
;
326 buffs
[0].cbBuffer
= strlen(message
);
327 buffs
[0].pvBuffer
= (PVOID
)message
;
329 /* to hold signature */
330 buffs
[1].BufferType
= SECBUFFER_TOKEN
;
331 buffs
[1].cbBuffer
= signature_buff_length
;
332 buffs
[1].pvBuffer
= signature_buff
;
334 ret
= MakeSignature(ctx
->ctx_sspi
,
338 if (ret
!= SEC_E_OK
) {
339 sip_sec_sspi_print_error("sip_sec_make_signature__sspi: MakeSignature", ret
);
340 g_free(signature_buff
);
344 signature
->value
= signature_buff
;
345 signature
->length
= buffs
[1].cbBuffer
;
351 * @param message a NULL terminated string to check signature of
352 * @return TRUE on success
355 sip_sec_verify_signature__sspi(SipSecContext context
,
356 const gchar
*message
,
357 SipSecBuffer signature
)
359 SecBufferDesc buffs_desc
;
363 buffs_desc
.cBuffers
= 2;
364 buffs_desc
.pBuffers
= buffs
;
365 buffs_desc
.ulVersion
= SECBUFFER_VERSION
;
367 /* message to sign */
368 buffs
[0].BufferType
= SECBUFFER_DATA
;
369 buffs
[0].cbBuffer
= strlen(message
);
370 buffs
[0].pvBuffer
= (PVOID
)message
;
372 /* signature to check */
373 buffs
[1].BufferType
= SECBUFFER_TOKEN
;
374 buffs
[1].cbBuffer
= signature
.length
;
375 buffs
[1].pvBuffer
= signature
.value
;
377 ret
= VerifySignature(((context_sspi
)context
)->ctx_sspi
,
382 if (ret
!= SEC_E_OK
) {
383 sip_sec_sspi_print_error("sip_sec_verify_signature__sspi: VerifySignature", ret
);
391 sip_sec_create_context__sspi(guint type
)
393 context_sspi context
= g_malloc0(sizeof(struct _context_sspi
));
394 if (!context
) return(NULL
);
396 context
->common
.acquire_cred_func
= sip_sec_acquire_cred__sspi
;
397 context
->common
.init_context_func
= sip_sec_init_sec_context__sspi
;
398 context
->common
.destroy_context_func
= sip_sec_destroy_sec_context__sspi
;
399 context
->common
.make_signature_func
= sip_sec_make_signature__sspi
;
400 context
->common
.verify_signature_func
= sip_sec_verify_signature__sspi
;
401 context
->type
= type
;
403 return((SipSecContext
) context
);
406 gboolean
sip_sec_password__sspi(void)
408 /* SSPI supports Single-Sign On */
412 /* Utility Functions */
415 * Returns interval in seconds from now till provided value
418 sip_sec_get_interval_from_now_sec(TimeStamp timestamp
)
422 ULARGE_INTEGER uliNow
, uliTo
;
424 GetLocalTime(&stNow
);
425 SystemTimeToFileTime(&stNow
, &ftNow
);
427 uliNow
.LowPart
= ftNow
.dwLowDateTime
;
428 uliNow
.HighPart
= ftNow
.dwHighDateTime
;
430 uliTo
.LowPart
= timestamp
.LowPart
;
431 uliTo
.HighPart
= timestamp
.HighPart
;
433 return (int)((uliTo
.QuadPart
- uliNow
.QuadPart
)/10/1000/1000);
437 sip_sec_sspi_print_error(const gchar
*func
,
440 gchar
*error_message
;
444 buff_length
= FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
|
445 FORMAT_MESSAGE_ALLOCATE_BUFFER
|
446 FORMAT_MESSAGE_IGNORE_INSERTS
,
453 error_message
= g_strndup(buff
, buff_length
);
456 SIPE_DEBUG_ERROR("SSPI ERROR [%d] in %s: %s", (int)ret
, func
, error_message
);
457 g_free(error_message
);