webticket: implement caching
[siplcs.git] / src / core / sip-sec-sspi.c
blob5263d2511e126d613c3d242777235d1b9c8abe0d
1 /**
2 * @file sip-sec-sspi.c
4 * pidgin-sipe
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
25 #include <stdio.h>
26 #include <windows.h>
27 #include <rpc.h>
28 #ifndef SECURITY_WIN32
29 #define SECURITY_WIN32 1
30 #endif
31 #include <security.h>
33 #include <glib.h>
35 #include "sip-sec.h"
36 #include "sip-sec-mech.h"
37 #include "sip-sec-sspi.h"
38 #include "sipe-backend.h"
40 /* Mechanism names */
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
48 #endif
50 typedef struct _context_sspi {
51 struct sip_sec_context common;
52 CredHandle* cred_sspi;
53 CtxtHandle* ctx_sspi;
54 /** Kerberos or NTLM */
55 const char *mech;
56 } *context_sspi;
58 static int
59 sip_sec_get_interval_from_now_sec(TimeStamp timestamp);
61 void
62 sip_sec_sspi_print_error(const char *func,
63 SECURITY_STATUS ret);
65 /** internal method */
66 static void
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 */
77 static sip_uint32
78 sip_sec_acquire_cred__sspi(SipSecContext context,
79 const char *domain,
80 const char *username,
81 const char *password)
83 SECURITY_STATUS ret;
84 TimeStamp expiry;
85 SEC_WINNT_AUTH_IDENTITY auth_identity;
86 context_sspi ctx = (context_sspi)context;
88 if (username) {
89 if (!password) {
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,
116 NULL,
117 (context->sso || !username) ? NULL : &auth_identity,
118 NULL,
119 NULL,
120 ctx->cred_sspi,
121 &expiry);
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;
128 } else {
129 return SIP_SEC_E_OK;
133 static sip_uint32
134 sip_sec_init_sec_context__sspi(SipSecContext context,
135 SipSecBuffer in_buff,
136 SipSecBuffer *out_buff,
137 const char *service_name)
139 TimeStamp expiry;
140 SecBufferDesc input_desc, output_desc;
141 SecBuffer in_token, out_token;
142 SECURITY_STATUS ret;
143 ULONG req_flags;
144 ULONG ret_flags;
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;
154 /* input token */
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 |
169 ISC_REQ_INTEGRITY |
170 ISC_REQ_IDENTIFY);
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,
179 ctx->ctx_sspi,
180 (SEC_CHAR *)service_name,
181 req_flags,
183 SECURITY_NATIVE_DREP,
184 &input_desc,
186 out_context,
187 &output_desc,
188 &ret_flags,
189 &expiry);
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;
212 } else {
213 return SIP_SEC_E_OK;
217 static void
218 sip_sec_destroy_sec_context__sspi(SipSecContext context)
220 sip_sec_destroy_sspi_context((context_sspi)context);
221 g_free(context);
225 * @param message a NULL terminated string to sign
228 static sip_uint32
229 sip_sec_make_signature__sspi(SipSecContext context,
230 const char *message,
231 SipSecBuffer *signature)
233 SecBufferDesc buffs_desc;
234 SecBuffer buffs[2];
235 SECURITY_STATUS ret;
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,
242 SECPKG_ATTR_SIZES,
243 &context_sizes);
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,
268 (ULONG)0,
269 &buffs_desc,
270 100);
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;
280 return SIP_SEC_E_OK;
284 * @param message a NULL terminated string to check signature of
285 * @return SIP_SEC_E_OK on success
287 static sip_uint32
288 sip_sec_verify_signature__sspi(SipSecContext context,
289 const char *message,
290 SipSecBuffer signature)
292 SecBufferDesc buffs_desc;
293 SecBuffer buffs[2];
294 SECURITY_STATUS ret;
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,
311 &buffs_desc,
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;
320 return SIP_SEC_E_OK;
323 SipSecContext
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
346 static int
347 sip_sec_get_interval_from_now_sec(TimeStamp timestamp)
349 SYSTEMTIME stNow;
350 FILETIME ftNow;
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);
365 void
366 sip_sec_sspi_print_error(const char *func,
367 SECURITY_STATUS ret)
369 char *error_message;
370 static char *buff;
371 int buff_length;
373 buff_length = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
374 FORMAT_MESSAGE_ALLOCATE_BUFFER |
375 FORMAT_MESSAGE_IGNORE_INSERTS,
377 ret,
379 (LPTSTR)&buff,
380 16384,
382 error_message = g_strndup(buff, buff_length);
383 LocalFree(buff);
385 printf("SSPI ERROR [%d] in %s: %s", (int)ret, func, error_message);
386 g_free(error_message);
391 Local Variables:
392 mode: c
393 c-file-style: "bsd"
394 indent-tabs-mode: t
395 tab-width: 8
396 End: