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
27 #ifndef SECURITY_WIN32
28 #define SECURITY_WIN32 1
35 #include "sip-sec-mech.h"
36 #include "sip-sec-sspi.h"
37 #include "sipe-backend.h"
38 #include "sipe-core.h"
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
54 typedef struct _context_sspi
{
55 struct sip_sec_context common
;
56 CredHandle
* cred_sspi
;
60 gboolean connection_less_ntlm
;
64 sip_sec_get_interval_from_now_sec(TimeStamp timestamp
);
67 sip_sec_sspi_print_error(const char *func
,
70 /** internal method */
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 */
89 sip_sec_acquire_cred__sspi(SipSecContext context
,
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
);
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
,
131 context
->sso
? NULL
: &auth_identity
,
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
;
148 sip_sec_init_sec_context__sspi(SipSecContext context
,
149 SipSecBuffer in_buff
,
150 SipSecBuffer
*out_buff
,
151 const char *service_name
)
154 SecBufferDesc input_desc
, output_desc
;
155 SecBuffer in_token
, out_token
;
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
) {
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("");
173 /* call again to create context for connection-less NTLM */
175 SipSecBuffer empty
= { 0, NULL
};
177 ctx
->initial
= FALSE
;
178 ret
= sip_sec_init_sec_context__sspi(context
,
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
);
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
&&
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
;
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
|
230 if (ctx
->connection_less_ntlm
) {
231 req_flags
|= (ISC_REQ_DATAGRAM
);
234 ret
= InitializeSecurityContextA(ctx
->cred_sspi
,
236 (SEC_CHAR
*)service_name
,
239 SECURITY_NATIVE_DREP
,
247 if (ret
!= SEC_E_OK
&& ret
!= SEC_I_CONTINUE_NEEDED
) {
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
;
278 sip_sec_destroy_sec_context__sspi(SipSecContext context
)
280 sip_sec_destroy_sspi_context((context_sspi
)context
);
285 * @param message a NULL terminated string to sign
289 sip_sec_make_signature__sspi(SipSecContext context
,
291 SipSecBuffer
*signature
)
293 SecBufferDesc buffs_desc
;
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
,
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
,
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
;
344 * @param message a NULL terminated string to check signature of
345 * @return SIP_SEC_E_OK on success
348 sip_sec_verify_signature__sspi(SipSecContext context
,
350 SipSecBuffer signature
)
352 SecBufferDesc buffs_desc
;
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
,
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
;
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 */
406 /* Utility Functions */
409 * Returns interval in seconds from now till provided value
412 sip_sec_get_interval_from_now_sec(TimeStamp timestamp
)
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);
431 sip_sec_sspi_print_error(const char *func
,
438 buff_length
= FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
|
439 FORMAT_MESSAGE_ALLOCATE_BUFFER
|
440 FORMAT_MESSAGE_IGNORE_INSERTS
,
447 error_message
= g_strndup(buff
, buff_length
);
450 SIPE_DEBUG_ERROR("SSPI ERROR [%d] in %s: %s", (int)ret
, func
, error_message
);
451 g_free(error_message
);