6 * Copyright (C) 2011-2015 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
40 #include "sipe-common.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"
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
63 typedef struct _context_sspi
{
64 struct sip_sec_context common
;
65 CredHandle
* cred_sspi
;
69 #define SIP_SEC_FLAG_SSPI_SIP_NTLM 0x00010000
71 /* Utility Functions */
74 sip_sec_sspi_print_error(const gchar
*func
,
81 buff_length
= FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
|
82 FORMAT_MESSAGE_ALLOCATE_BUFFER
|
83 FORMAT_MESSAGE_IGNORE_INSERTS
,
90 error_message
= g_strndup(buff
, buff_length
);
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 */
99 sip_sec_get_interval_from_now_sec(TimeStamp timestamp
)
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);
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 */
135 sip_sec_acquire_cred__sspi(SipSecContext context
,
136 const gchar
*username
,
137 const gchar
*password
)
141 SEC_WINNT_AUTH_IDENTITY auth_identity
;
142 context_sspi ctx
= (context_sspi
)context
;
143 gchar
*domain_tmp
= NULL
;
144 gchar
*user_tmp
= NULL
;
146 /* this is the first time we are allowed to set private flags */
147 if (((context
->flags
& SIP_SEC_FLAG_COMMON_HTTP
) == 0) &&
148 (context
->type
== SIPE_AUTHENTICATION_TYPE_NTLM
))
149 context
->flags
|= SIP_SEC_FLAG_SSPI_SIP_NTLM
;
151 if ((context
->flags
& SIP_SEC_FLAG_COMMON_SSO
) == 0) {
152 if (is_empty(username
) || is_empty(password
)) {
153 SIPE_DEBUG_ERROR_NOFORMAT("sip_sec_acquire_cred__sspi: no valid authentication information provided");
157 memset(&auth_identity
, 0, sizeof(auth_identity
));
158 auth_identity
.Flags
= SEC_WINNT_AUTH_IDENTITY_ANSI
;
160 if (SIP_SEC_USERNAME_IS_ENTERPRISE
) {
161 /* use username as-is, just replace enterprise marker with @ */
162 user_tmp
= sipe_utils_str_replace(username
,
163 SIP_SEC_USERNAME_ENTERPRISE_STRING
,
166 SIP_SEC_USERNAME_SPLIT_START
;
167 if (SIP_SEC_USERNAME_HAS_DOMAIN
) {
168 domain_tmp
= g_strdup(SIP_SEC_USERNAME_DOMAIN
);
169 user_tmp
= g_strdup(SIP_SEC_USERNAME_ACCOUNT
);
170 auth_identity
.Domain
= (unsigned char *)domain_tmp
;
171 auth_identity
.DomainLength
= strlen(domain_tmp
);
173 SIP_SEC_USERNAME_SPLIT_END
;
176 auth_identity
.User
= (unsigned char *)(user_tmp
? user_tmp
: username
);
177 auth_identity
.UserLength
= strlen((char *) auth_identity
.User
);
179 auth_identity
.Password
= (unsigned char *)password
;
180 auth_identity
.PasswordLength
= strlen(password
);
183 ctx
->cred_sspi
= g_malloc0(sizeof(CredHandle
));
185 ret
= AcquireCredentialsHandleA(NULL
,
186 (SEC_CHAR
*)mech_names
[context
->type
],
187 SECPKG_CRED_OUTBOUND
,
189 (context
->flags
& SIP_SEC_FLAG_COMMON_SSO
) ? NULL
: &auth_identity
,
198 if (ret
!= SEC_E_OK
) {
199 sip_sec_sspi_print_error("sip_sec_acquire_cred__sspi: AcquireCredentialsHandleA", ret
);
200 g_free(ctx
->cred_sspi
);
201 ctx
->cred_sspi
= NULL
;
209 sip_sec_init_sec_context__sspi(SipSecContext context
,
210 SipSecBuffer in_buff
,
211 SipSecBuffer
*out_buff
,
212 const gchar
*service_name
)
215 SecBufferDesc input_desc
, output_desc
;
216 SecBuffer in_token
, out_token
;
220 context_sspi ctx
= (context_sspi
)context
;
221 CtxtHandle
* out_context
;
223 SIPE_DEBUG_INFO_NOFORMAT("sip_sec_init_sec_context__sspi: in use");
226 * If authentication was already completed, then this mean a new
227 * authentication handshake has started on the existing connection.
228 * We must throw away the old context, because we need a new one.
230 if ((context
->flags
& SIP_SEC_FLAG_COMMON_READY
) &&
232 SIPE_DEBUG_INFO_NOFORMAT("sip_sec_init_sec_context__sspi: dropping old context");
233 DeleteSecurityContext(ctx
->ctx_sspi
);
234 g_free(ctx
->ctx_sspi
);
235 ctx
->ctx_sspi
= NULL
;
236 context
->flags
&= ~SIP_SEC_FLAG_COMMON_READY
;
239 /* reuse existing context on following calls */
240 out_context
= ctx
->ctx_sspi
? ctx
->ctx_sspi
: g_malloc0(sizeof(CtxtHandle
));
242 input_desc
.cBuffers
= 1;
243 input_desc
.pBuffers
= &in_token
;
244 input_desc
.ulVersion
= SECBUFFER_VERSION
;
247 in_token
.BufferType
= SECBUFFER_TOKEN
;
248 in_token
.cbBuffer
= in_buff
.length
;
249 in_token
.pvBuffer
= in_buff
.value
;
251 output_desc
.cBuffers
= 1;
252 output_desc
.pBuffers
= &out_token
;
253 output_desc
.ulVersion
= SECBUFFER_VERSION
;
255 /* to hold output token */
256 out_token
.BufferType
= SECBUFFER_TOKEN
;
257 out_token
.cbBuffer
= 0;
258 out_token
.pvBuffer
= NULL
;
260 req_flags
= (ISC_REQ_ALLOCATE_MEMORY
|
264 if (context
->flags
& SIP_SEC_FLAG_SSPI_SIP_NTLM
) {
265 req_flags
|= (ISC_REQ_DATAGRAM
);
268 ret
= InitializeSecurityContextA(ctx
->cred_sspi
,
270 (SEC_CHAR
*)service_name
,
273 SECURITY_NATIVE_DREP
,
281 if (ret
!= SEC_E_OK
&& ret
!= SEC_I_CONTINUE_NEEDED
) {
284 sip_sec_destroy_sspi_context(ctx
);
285 sip_sec_sspi_print_error("sip_sec_init_sec_context__sspi: InitializeSecurityContextA", ret
);
289 out_buff
->length
= out_token
.cbBuffer
;
290 if (out_token
.cbBuffer
) {
291 out_buff
->value
= g_malloc(out_token
.cbBuffer
);
292 memcpy(out_buff
->value
, out_token
.pvBuffer
, out_token
.cbBuffer
);
294 /* Special case: empty token */
295 out_buff
->value
= (guint8
*) g_strdup("");
297 FreeContextBuffer(out_token
.pvBuffer
);
299 ctx
->ctx_sspi
= out_context
;
301 if (context
->type
== SIPE_AUTHENTICATION_TYPE_KERBEROS
) {
302 context
->expires
= sip_sec_get_interval_from_now_sec(expiry
);
305 if (ret
!= SEC_I_CONTINUE_NEEDED
) {
306 /* Authentication is completed */
307 context
->flags
|= SIP_SEC_FLAG_COMMON_READY
;
314 sip_sec_destroy_sec_context__sspi(SipSecContext context
)
316 sip_sec_destroy_sspi_context((context_sspi
)context
);
321 * @param message a NULL terminated string to sign
325 sip_sec_make_signature__sspi(SipSecContext context
,
326 const gchar
*message
,
327 SipSecBuffer
*signature
)
329 SecBufferDesc buffs_desc
;
332 SecPkgContext_Sizes context_sizes
;
333 guchar
*signature_buff
;
334 size_t signature_buff_length
;
335 context_sspi ctx
= (context_sspi
) context
;
337 ret
= QueryContextAttributes(ctx
->ctx_sspi
,
341 if (ret
!= SEC_E_OK
) {
342 sip_sec_sspi_print_error("sip_sec_make_signature__sspi: QueryContextAttributes", ret
);
346 signature_buff_length
= context_sizes
.cbMaxSignature
;
347 signature_buff
= g_malloc(signature_buff_length
);
349 buffs_desc
.cBuffers
= 2;
350 buffs_desc
.pBuffers
= buffs
;
351 buffs_desc
.ulVersion
= SECBUFFER_VERSION
;
353 /* message to sign */
354 buffs
[0].BufferType
= SECBUFFER_DATA
;
355 buffs
[0].cbBuffer
= strlen(message
);
356 buffs
[0].pvBuffer
= (PVOID
)message
;
358 /* to hold signature */
359 buffs
[1].BufferType
= SECBUFFER_TOKEN
;
360 buffs
[1].cbBuffer
= signature_buff_length
;
361 buffs
[1].pvBuffer
= signature_buff
;
363 ret
= MakeSignature(ctx
->ctx_sspi
,
367 if (ret
!= SEC_E_OK
) {
368 sip_sec_sspi_print_error("sip_sec_make_signature__sspi: MakeSignature", ret
);
369 g_free(signature_buff
);
373 signature
->value
= signature_buff
;
374 signature
->length
= buffs
[1].cbBuffer
;
380 * @param message a NULL terminated string to check signature of
381 * @return TRUE on success
384 sip_sec_verify_signature__sspi(SipSecContext context
,
385 const gchar
*message
,
386 SipSecBuffer signature
)
388 SecBufferDesc buffs_desc
;
392 buffs_desc
.cBuffers
= 2;
393 buffs_desc
.pBuffers
= buffs
;
394 buffs_desc
.ulVersion
= SECBUFFER_VERSION
;
396 /* message to sign */
397 buffs
[0].BufferType
= SECBUFFER_DATA
;
398 buffs
[0].cbBuffer
= strlen(message
);
399 buffs
[0].pvBuffer
= (PVOID
)message
;
401 /* signature to check */
402 buffs
[1].BufferType
= SECBUFFER_TOKEN
;
403 buffs
[1].cbBuffer
= signature
.length
;
404 buffs
[1].pvBuffer
= signature
.value
;
406 ret
= VerifySignature(((context_sspi
)context
)->ctx_sspi
,
411 if (ret
!= SEC_E_OK
) {
412 sip_sec_sspi_print_error("sip_sec_verify_signature__sspi: VerifySignature", ret
);
419 /* SSPI implements SPNEGO (RFC 4559) */
421 sip_sec_context_name__sspi(SipSecContext context
)
423 return(mech_names
[context
->type
]);
427 sip_sec_create_context__sspi(SIPE_UNUSED_PARAMETER guint type
)
429 context_sspi context
= g_malloc0(sizeof(struct _context_sspi
));
430 if (!context
) return(NULL
);
432 context
->common
.acquire_cred_func
= sip_sec_acquire_cred__sspi
;
433 context
->common
.init_context_func
= sip_sec_init_sec_context__sspi
;
434 context
->common
.destroy_context_func
= sip_sec_destroy_sec_context__sspi
;
435 context
->common
.make_signature_func
= sip_sec_make_signature__sspi
;
436 context
->common
.verify_signature_func
= sip_sec_verify_signature__sspi
;
437 context
->common
.context_name_func
= sip_sec_context_name__sspi
;
439 return((SipSecContext
) context
);
442 gboolean
sip_sec_password__sspi(void)
444 /* SSPI supports Single-Sign On */