mingw: fixed Windows build
[siplcs.git] / src / core / sip-sec-sspi.c
blobfb5a499111b3445399ae1c7876d6bceebe6d2c62
1 /**
2 * @file sip-sec-sspi.c
4 * pidgin-sipe
6 * Copyright (C) 2009 pier11 <pier11@operamail.com>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 #include <windows.h>
25 #include <rpc.h>
26 #include <security.h>
28 #include <glib.h>
29 #include <stdio.h>
31 #include "debug.h"
33 #include "sip-sec.h"
34 #include "sip-sec-mech.h"
35 #include "sip-sec-sspi.h"
37 /* Mechanism names */
38 #define SSPI_MECH_NTLM "NTLM"
39 #define SSPI_MECH_KERBEROS "Kerberos"
40 #define SSPI_MECH_NEGOTIATE "Negotiate"
42 #define ISC_REQ_IDENTIFY 0x00002000
44 typedef struct _context_sspi {
45 struct sip_sec_context common;
46 CredHandle* cred_sspi;
47 CtxtHandle* ctx_sspi;
48 /** Kerberos or NTLM */
49 const char *mech;
50 } *context_sspi;
52 static int
53 sip_sec_get_interval_from_now_sec(TimeStamp timestamp);
55 void
56 sip_sec_sspi_print_error(const char *func,
57 SECURITY_STATUS ret);
59 /** internal method */
60 static void
61 sip_sec_destroy_sspi_context(context_sspi context)
63 if (context->ctx_sspi)
64 DeleteSecurityContext(context->ctx_sspi);
65 if (context->cred_sspi)
66 FreeCredentialsHandle(context->cred_sspi);
69 /* sip-sec-mech.h API implementation for SSPI - Kerberos and NTLM */
71 static sip_uint32
72 sip_sec_acquire_cred__sspi(SipSecContext context,
73 const char *domain,
74 const char *username,
75 const char *password)
77 SECURITY_STATUS ret;
78 TimeStamp expiry;
79 SEC_WINNT_AUTH_IDENTITY auth_identity;
80 context_sspi ctx = (context_sspi)context;
81 CredHandle *cred_handle;
83 if (username) {
84 if (!password) {
85 return SIP_SEC_E_INTERNAL_ERROR;
88 memset(&auth_identity, 0, sizeof(auth_identity));
89 auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
91 if ( domain && (strlen(domain) > 0) ) {
92 auth_identity.Domain = (unsigned char*)domain;
93 auth_identity.DomainLength = strlen(auth_identity.Domain);
96 auth_identity.User = (unsigned char*)username;
97 auth_identity.UserLength = strlen(auth_identity.User);
99 auth_identity.Password = (unsigned char*)password;
100 auth_identity.PasswordLength = strlen(auth_identity.Password);
103 cred_handle = g_malloc0(sizeof(CredHandle));
105 ret = AcquireCredentialsHandle( NULL,
106 (SEC_CHAR *)ctx->mech,
107 SECPKG_CRED_OUTBOUND,
108 NULL,
109 (context->sso || !username) ? NULL : &auth_identity,
110 NULL,
111 NULL,
112 cred_handle,
113 &expiry
116 if (ret != SEC_E_OK) {
117 sip_sec_sspi_print_error("sip_sec_acquire_cred__sspi: AcquireCredentialsHandle", ret);
118 ctx->cred_sspi = NULL;
119 return SIP_SEC_E_INTERNAL_ERROR;
120 } else {
121 ctx->cred_sspi = cred_handle;
122 return SIP_SEC_E_OK;
126 static sip_uint32
127 sip_sec_init_sec_context__sspi(SipSecContext context,
128 SipSecBuffer in_buff,
129 SipSecBuffer *out_buff,
130 const char *service_name)
132 TimeStamp expiry;
133 SecBufferDesc input_desc, output_desc;
134 SecBuffer in_token, out_token;
135 SECURITY_STATUS ret;
136 ULONG req_flags;
137 ULONG ret_flags;
138 context_sspi ctx = (context_sspi)context;
139 CtxtHandle* out_context = g_malloc0(sizeof(CtxtHandle));
141 purple_debug_info("sipe", "sip_sec_init_sec_context__sspi: in use\n");
143 input_desc.cBuffers = 1;
144 input_desc.pBuffers = &in_token;
145 input_desc.ulVersion = SECBUFFER_VERSION;
147 /* input token */
148 in_token.BufferType = SECBUFFER_TOKEN;
149 in_token.cbBuffer = in_buff.length;
150 in_token.pvBuffer = in_buff.value;
152 output_desc.cBuffers = 1;
153 output_desc.pBuffers = &out_token;
154 output_desc.ulVersion = SECBUFFER_VERSION;
156 /* to hold output token */
157 out_token.BufferType = SECBUFFER_TOKEN;
158 out_token.cbBuffer = 0;
159 out_token.pvBuffer = NULL;
161 req_flags = (ISC_REQ_ALLOCATE_MEMORY |
162 ISC_REQ_INTEGRITY |
163 ISC_REQ_IDENTIFY);
165 if (ctx->mech && !strcmp(ctx->mech, SSPI_MECH_NTLM) &&
166 !context->is_connection_based)
168 req_flags |= (ISC_REQ_DATAGRAM);
171 ret = InitializeSecurityContext(ctx->cred_sspi,
172 ctx->ctx_sspi,
173 (SEC_CHAR *)service_name,
174 req_flags,
176 SECURITY_NATIVE_DREP,
177 &input_desc,
179 out_context,
180 &output_desc,
181 &ret_flags,
182 &expiry
185 if (ret != SEC_E_OK && ret != SEC_I_CONTINUE_NEEDED) {
186 sip_sec_destroy_sspi_context(ctx);
187 sip_sec_sspi_print_error("sip_sec_init_sec_context__sspi: InitializeSecurityContext", ret);
188 return SIP_SEC_E_INTERNAL_ERROR;
191 out_buff->length = out_token.cbBuffer;
192 out_buff->value = NULL;
193 if (out_token.cbBuffer) {
194 out_buff->value = g_malloc0(out_token.cbBuffer);
195 memmove(out_buff->value, out_token.pvBuffer, out_token.cbBuffer);
196 FreeContextBuffer(out_token.pvBuffer);
199 ctx->ctx_sspi = out_context;
200 if (ctx->mech && !strcmp(ctx->mech, SSPI_MECH_KERBEROS)) {
201 context->expires = sip_sec_get_interval_from_now_sec(expiry);
204 if (ret == SEC_I_CONTINUE_NEEDED) {
205 return SIP_SEC_I_CONTINUE_NEEDED;
206 } else {
207 return SIP_SEC_E_OK;
211 static void
212 sip_sec_destroy_sec_context__sspi(SipSecContext context)
214 sip_sec_destroy_sspi_context((context_sspi)context);
215 g_free(context);
219 * @param message a NULL terminated string to sign
222 static sip_uint32
223 sip_sec_make_signature__sspi(SipSecContext context,
224 const char *message,
225 SipSecBuffer *signature)
227 SecBufferDesc buffs_desc;
228 SecBuffer buffs[2];
229 SECURITY_STATUS ret;
230 SecPkgContext_Sizes context_sizes;
231 unsigned char *signature_buff;
232 size_t signature_buff_length;
233 context_sspi ctx = (context_sspi) context;
235 ret = QueryContextAttributes(ctx->ctx_sspi,
236 SECPKG_ATTR_SIZES,
237 &context_sizes);
239 if (ret != SEC_E_OK) {
240 sip_sec_sspi_print_error("sip_sec_make_signature__sspi: QueryContextAttributes", ret);
241 return SIP_SEC_E_INTERNAL_ERROR;
244 signature_buff_length = context_sizes.cbMaxSignature;
245 signature_buff = g_malloc0(signature_buff_length);
247 buffs_desc.cBuffers = 2;
248 buffs_desc.pBuffers = buffs;
249 buffs_desc.ulVersion = SECBUFFER_VERSION;
251 /* message to sign */
252 buffs[0].BufferType = SECBUFFER_DATA;
253 buffs[0].cbBuffer = strlen(message);
254 buffs[0].pvBuffer = (PVOID)message;
256 /* to hold signature */
257 buffs[1].BufferType = SECBUFFER_TOKEN;
258 buffs[1].cbBuffer = signature_buff_length;
259 buffs[1].pvBuffer = signature_buff;
261 ret = MakeSignature(ctx->ctx_sspi,
262 (ULONG)0,
263 &buffs_desc,
264 100);
265 if (ret != SEC_E_OK) {
266 sip_sec_sspi_print_error("sip_sec_make_signature__sspi: MakeSignature", ret);
267 g_free(signature_buff);
268 return SIP_SEC_E_INTERNAL_ERROR;
271 signature->value = signature_buff;
272 signature->length = buffs[1].cbBuffer;
274 return SIP_SEC_E_OK;
278 * @param message a NULL terminated string to check signature of
279 * @return SIP_SEC_E_OK on success
281 static sip_uint32
282 sip_sec_verify_signature__sspi(SipSecContext context,
283 const char *message,
284 SipSecBuffer signature)
286 SecBufferDesc buffs_desc;
287 SecBuffer buffs[2];
288 SECURITY_STATUS ret;
290 buffs_desc.cBuffers = 2;
291 buffs_desc.pBuffers = buffs;
292 buffs_desc.ulVersion = SECBUFFER_VERSION;
294 /* message to sign */
295 buffs[0].BufferType = SECBUFFER_DATA;
296 buffs[0].cbBuffer = strlen(message);
297 buffs[0].pvBuffer = (PVOID)message;
299 /* signature to check */
300 buffs[1].BufferType = SECBUFFER_TOKEN;
301 buffs[1].cbBuffer = signature.length;
302 buffs[1].pvBuffer = signature.value;
304 ret = VerifySignature(((context_sspi)context)->ctx_sspi,
305 &buffs_desc,
309 if (ret != SEC_E_OK) {
310 sip_sec_sspi_print_error("sip_sec_verify_signature__sspi: VerifySignature", ret);
311 return SIP_SEC_E_INTERNAL_ERROR;
314 return SIP_SEC_E_OK;
317 SipSecContext
318 sip_sec_create_context__sspi(SipSecAuthType type)
320 context_sspi context = g_malloc0(sizeof(struct _context_sspi));
321 if (!context) return(NULL);
323 context->common.acquire_cred_func = sip_sec_acquire_cred__sspi;
324 context->common.init_context_func = sip_sec_init_sec_context__sspi;
325 context->common.destroy_context_func = sip_sec_destroy_sec_context__sspi;
326 context->common.make_signature_func = sip_sec_make_signature__sspi;
327 context->common.verify_signature_func = sip_sec_verify_signature__sspi;
328 context->mech = (type == AUTH_TYPE_NTLM) ? SSPI_MECH_NTLM :
329 ((type == AUTH_TYPE_KERBEROS) ? SSPI_MECH_KERBEROS : SSPI_MECH_NEGOTIATE);
331 return((SipSecContext) context);
334 /* Utility Functions */
336 /**
337 * Returns interval in seconds from now till provided value
339 static int
340 sip_sec_get_interval_from_now_sec(TimeStamp timestamp)
342 SYSTEMTIME stNow;
343 FILETIME ftNow;
344 ULARGE_INTEGER uliNow, uliTo;
346 GetLocalTime(&stNow);
347 SystemTimeToFileTime(&stNow, &ftNow);
349 uliNow.LowPart = ftNow.dwLowDateTime;
350 uliNow.HighPart = ftNow.dwHighDateTime;
352 uliTo.LowPart = timestamp.LowPart;
353 uliTo.HighPart = timestamp.HighPart;
355 return (int)((uliTo.QuadPart - uliNow.QuadPart)/10/1000/1000);
358 void
359 sip_sec_sspi_print_error(const char *func,
360 SECURITY_STATUS ret)
362 char *error_message;
363 static char *buff;
364 int buff_length;
366 buff_length = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
367 FORMAT_MESSAGE_ALLOCATE_BUFFER |
368 FORMAT_MESSAGE_IGNORE_INSERTS,
370 ret,
372 (LPTSTR)&buff,
373 16384,
375 error_message = g_strndup(buff, buff_length);
376 LocalFree(buff);
378 printf("SSPI ERROR [%d] in %s: %s", (int)ret, func, error_message);
379 g_free(error_message);
384 Local Variables:
385 mode: c
386 c-file-style: "bsd"
387 indent-tabs-mode: t
388 tab-width: 8
389 End: