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
28 #ifndef SECURITY_WIN32
29 #define SECURITY_WIN32 1
36 #include "sip-sec-mech.h"
37 #include "sip-sec-sspi.h"
38 #include "sipe-backend.h"
41 #define SSPI_MECH_NTLM "NTLM"
42 #define SSPI_MECH_KERBEROS "Kerberos"
43 #define SSPI_MECH_NEGOTIATE "Negotiate"
44 #define SSPI_MECH_TLS_DSK "Schannel" /* SSL/TLS provider, is this correct? */
46 #ifndef ISC_REQ_IDENTIFY
47 #define ISC_REQ_IDENTIFY 0x00002000
50 typedef struct _context_sspi
{
51 struct sip_sec_context common
;
52 CredHandle
* cred_sspi
;
54 /** Kerberos or NTLM */
59 sip_sec_get_interval_from_now_sec(TimeStamp timestamp
);
62 sip_sec_sspi_print_error(const char *func
,
65 /** internal method */
67 sip_sec_destroy_sspi_context(context_sspi context
)
69 if (context
->ctx_sspi
)
70 DeleteSecurityContext(context
->ctx_sspi
);
71 if (context
->cred_sspi
)
72 FreeCredentialsHandle(context
->cred_sspi
);
75 /* sip-sec-mech.h API implementation for SSPI - Kerberos and NTLM */
78 sip_sec_acquire_cred__sspi(SipSecContext context
,
85 SEC_WINNT_AUTH_IDENTITY auth_identity
;
86 context_sspi ctx
= (context_sspi
)context
;
90 return SIP_SEC_E_INTERNAL_ERROR
;
93 memset(&auth_identity
, 0, sizeof(auth_identity
));
94 auth_identity
.Flags
= SEC_WINNT_AUTH_IDENTITY_ANSI
;
96 if ( domain
&& (strlen(domain
) > 0) ) {
97 auth_identity
.Domain
= (unsigned char*)domain
;
98 auth_identity
.DomainLength
= strlen(domain
);
101 auth_identity
.User
= (unsigned char*)username
;
102 auth_identity
.UserLength
= strlen(username
);
104 auth_identity
.Password
= (unsigned char*)password
;
105 auth_identity
.PasswordLength
= strlen(password
);
108 ctx
->cred_sspi
= g_malloc0(sizeof(CredHandle
));
110 /* @TODO: this does not work for "Schannel" (TLS-DSK) as it expects
111 a SCHANNEL_CRED datastructure, pointing to the private key
112 and the client certificate */
113 ret
= AcquireCredentialsHandleA(NULL
,
114 (SEC_CHAR
*)ctx
->mech
,
115 SECPKG_CRED_OUTBOUND
,
117 (context
->sso
|| !username
) ? NULL
: &auth_identity
,
123 if (ret
!= SEC_E_OK
) {
124 sip_sec_sspi_print_error("sip_sec_acquire_cred__sspi: AcquireCredentialsHandleA", ret
);
125 g_free(ctx
->cred_sspi
);
126 ctx
->cred_sspi
= NULL
;
127 return SIP_SEC_E_INTERNAL_ERROR
;
134 sip_sec_init_sec_context__sspi(SipSecContext context
,
135 SipSecBuffer in_buff
,
136 SipSecBuffer
*out_buff
,
137 const char *service_name
)
140 SecBufferDesc input_desc
, output_desc
;
141 SecBuffer in_token
, out_token
;
145 context_sspi ctx
= (context_sspi
)context
;
146 CtxtHandle
* out_context
= g_malloc0(sizeof(CtxtHandle
));
148 SIPE_DEBUG_INFO_NOFORMAT("sip_sec_init_sec_context__sspi: in use");
150 input_desc
.cBuffers
= 1;
151 input_desc
.pBuffers
= &in_token
;
152 input_desc
.ulVersion
= SECBUFFER_VERSION
;
155 in_token
.BufferType
= SECBUFFER_TOKEN
;
156 in_token
.cbBuffer
= in_buff
.length
;
157 in_token
.pvBuffer
= in_buff
.value
;
159 output_desc
.cBuffers
= 1;
160 output_desc
.pBuffers
= &out_token
;
161 output_desc
.ulVersion
= SECBUFFER_VERSION
;
163 /* to hold output token */
164 out_token
.BufferType
= SECBUFFER_TOKEN
;
165 out_token
.cbBuffer
= 0;
166 out_token
.pvBuffer
= NULL
;
168 req_flags
= (ISC_REQ_ALLOCATE_MEMORY
|
172 if (ctx
->mech
&& !strcmp(ctx
->mech
, SSPI_MECH_NTLM
) &&
173 !context
->is_connection_based
)
175 req_flags
|= (ISC_REQ_DATAGRAM
);
178 ret
= InitializeSecurityContextA(ctx
->cred_sspi
,
180 (SEC_CHAR
*)service_name
,
183 SECURITY_NATIVE_DREP
,
191 if (ret
!= SEC_E_OK
&& ret
!= SEC_I_CONTINUE_NEEDED
) {
192 sip_sec_destroy_sspi_context(ctx
);
193 sip_sec_sspi_print_error("sip_sec_init_sec_context__sspi: InitializeSecurityContextA", ret
);
194 return SIP_SEC_E_INTERNAL_ERROR
;
197 out_buff
->length
= out_token
.cbBuffer
;
198 out_buff
->value
= NULL
;
199 if (out_token
.cbBuffer
) {
200 out_buff
->value
= g_malloc0(out_token
.cbBuffer
);
201 memmove(out_buff
->value
, out_token
.pvBuffer
, out_token
.cbBuffer
);
202 FreeContextBuffer(out_token
.pvBuffer
);
205 ctx
->ctx_sspi
= out_context
;
206 if (ctx
->mech
&& !strcmp(ctx
->mech
, SSPI_MECH_KERBEROS
)) {
207 context
->expires
= sip_sec_get_interval_from_now_sec(expiry
);
210 if (ret
== SEC_I_CONTINUE_NEEDED
) {
211 return SIP_SEC_I_CONTINUE_NEEDED
;
218 sip_sec_destroy_sec_context__sspi(SipSecContext context
)
220 sip_sec_destroy_sspi_context((context_sspi
)context
);
225 * @param message a NULL terminated string to sign
229 sip_sec_make_signature__sspi(SipSecContext context
,
231 SipSecBuffer
*signature
)
233 SecBufferDesc buffs_desc
;
236 SecPkgContext_Sizes context_sizes
;
237 unsigned char *signature_buff
;
238 size_t signature_buff_length
;
239 context_sspi ctx
= (context_sspi
) context
;
241 ret
= QueryContextAttributes(ctx
->ctx_sspi
,
245 if (ret
!= SEC_E_OK
) {
246 sip_sec_sspi_print_error("sip_sec_make_signature__sspi: QueryContextAttributes", ret
);
247 return SIP_SEC_E_INTERNAL_ERROR
;
250 signature_buff_length
= context_sizes
.cbMaxSignature
;
251 signature_buff
= g_malloc0(signature_buff_length
);
253 buffs_desc
.cBuffers
= 2;
254 buffs_desc
.pBuffers
= buffs
;
255 buffs_desc
.ulVersion
= SECBUFFER_VERSION
;
257 /* message to sign */
258 buffs
[0].BufferType
= SECBUFFER_DATA
;
259 buffs
[0].cbBuffer
= strlen(message
);
260 buffs
[0].pvBuffer
= (PVOID
)message
;
262 /* to hold signature */
263 buffs
[1].BufferType
= SECBUFFER_TOKEN
;
264 buffs
[1].cbBuffer
= signature_buff_length
;
265 buffs
[1].pvBuffer
= signature_buff
;
267 ret
= MakeSignature(ctx
->ctx_sspi
,
271 if (ret
!= SEC_E_OK
) {
272 sip_sec_sspi_print_error("sip_sec_make_signature__sspi: MakeSignature", ret
);
273 g_free(signature_buff
);
274 return SIP_SEC_E_INTERNAL_ERROR
;
277 signature
->value
= signature_buff
;
278 signature
->length
= buffs
[1].cbBuffer
;
284 * @param message a NULL terminated string to check signature of
285 * @return SIP_SEC_E_OK on success
288 sip_sec_verify_signature__sspi(SipSecContext context
,
290 SipSecBuffer signature
)
292 SecBufferDesc buffs_desc
;
296 buffs_desc
.cBuffers
= 2;
297 buffs_desc
.pBuffers
= buffs
;
298 buffs_desc
.ulVersion
= SECBUFFER_VERSION
;
300 /* message to sign */
301 buffs
[0].BufferType
= SECBUFFER_DATA
;
302 buffs
[0].cbBuffer
= strlen(message
);
303 buffs
[0].pvBuffer
= (PVOID
)message
;
305 /* signature to check */
306 buffs
[1].BufferType
= SECBUFFER_TOKEN
;
307 buffs
[1].cbBuffer
= signature
.length
;
308 buffs
[1].pvBuffer
= signature
.value
;
310 ret
= VerifySignature(((context_sspi
)context
)->ctx_sspi
,
315 if (ret
!= SEC_E_OK
) {
316 sip_sec_sspi_print_error("sip_sec_verify_signature__sspi: VerifySignature", ret
);
317 return SIP_SEC_E_INTERNAL_ERROR
;
324 sip_sec_create_context__sspi(guint type
)
326 context_sspi context
= g_malloc0(sizeof(struct _context_sspi
));
327 if (!context
) return(NULL
);
329 context
->common
.acquire_cred_func
= sip_sec_acquire_cred__sspi
;
330 context
->common
.init_context_func
= sip_sec_init_sec_context__sspi
;
331 context
->common
.destroy_context_func
= sip_sec_destroy_sec_context__sspi
;
332 context
->common
.make_signature_func
= sip_sec_make_signature__sspi
;
333 context
->common
.verify_signature_func
= sip_sec_verify_signature__sspi
;
334 context
->mech
= (type
== AUTH_TYPE_NTLM
) ? SSPI_MECH_NTLM
:
335 ((type
== AUTH_TYPE_KERBEROS
) ? SSPI_MECH_KERBEROS
:
336 ((type
== AUTH_TYPE_NEGOTIATE
) ? SSPI_MECH_NEGOTIATE
: SSPI_MECH_TLS_DSK
));
338 return((SipSecContext
) context
);
341 /* Utility Functions */
344 * Returns interval in seconds from now till provided value
347 sip_sec_get_interval_from_now_sec(TimeStamp timestamp
)
351 ULARGE_INTEGER uliNow
, uliTo
;
353 GetLocalTime(&stNow
);
354 SystemTimeToFileTime(&stNow
, &ftNow
);
356 uliNow
.LowPart
= ftNow
.dwLowDateTime
;
357 uliNow
.HighPart
= ftNow
.dwHighDateTime
;
359 uliTo
.LowPart
= timestamp
.LowPart
;
360 uliTo
.HighPart
= timestamp
.HighPart
;
362 return (int)((uliTo
.QuadPart
- uliNow
.QuadPart
)/10/1000/1000);
366 sip_sec_sspi_print_error(const char *func
,
373 buff_length
= FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
|
374 FORMAT_MESSAGE_ALLOCATE_BUFFER
|
375 FORMAT_MESSAGE_IGNORE_INSERTS
,
382 error_message
= g_strndup(buff
, buff_length
);
385 printf("SSPI ERROR [%d] in %s: %s", (int)ret
, func
, error_message
);
386 g_free(error_message
);