Updated to release 1.7.0
[siplcs.git] / src / sip-sec-sspi.c
blob40b0817af234f334313cd2b26a15707d2d055c6e
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 <security.h>
27 #include <glib.h>
28 #include <stdio.h>
30 #include "debug.h"
32 #include "sip-sec.h"
33 #include "sip-sec-mech.h"
34 #include "sip-sec-sspi.h"
36 /* Mechanism names */
37 #define SSPI_MECH_NTLM "NTLM"
38 #define SSPI_MECH_KERBEROS "Kerberos"
40 #define ISC_REQ_IDENTIFY 0x00002000
42 typedef struct _context_sspi {
43 struct sip_sec_context common;
44 CredHandle* cred_sspi;
45 CtxtHandle* ctx_sspi;
46 /** Kerberos or NTLM */
47 const char *mech;
48 } *context_sspi;
50 static int
51 sip_sec_get_interval_from_now_sec(TimeStamp timestamp);
53 void
54 sip_sec_sspi_print_error(const char *func,
55 SECURITY_STATUS ret);
57 /** internal method */
58 static void
59 sip_sec_destroy_sspi_context(context_sspi context)
61 if (context->ctx_sspi)
62 DeleteSecurityContext(context->ctx_sspi);
63 if (context->cred_sspi)
64 FreeCredentialsHandle(context->cred_sspi);
67 /* sip-sec-mech.h API implementation for SSPI - Kerberos and NTLM */
69 static sip_uint32
70 sip_sec_acquire_cred__sspi(SipSecContext context,
71 const char *domain,
72 const char *username,
73 const char *password)
75 SECURITY_STATUS ret;
76 TimeStamp expiry;
77 SEC_WINNT_AUTH_IDENTITY auth_identity;
78 context_sspi ctx = (context_sspi)context;
79 CredHandle *cred_handle = g_malloc0(sizeof(CredHandle));
81 if (username) {
82 if (!password) {
83 return SIP_SEC_E_INTERNAL_ERROR;
86 memset(&auth_identity, 0, sizeof(auth_identity));
87 auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
89 if ( domain && (strlen(domain) > 0) ) {
90 auth_identity.Domain = (unsigned char*)domain;
91 auth_identity.DomainLength = strlen(auth_identity.Domain);
94 auth_identity.User = (unsigned char*)username;
95 auth_identity.UserLength = strlen(auth_identity.User);
97 auth_identity.Password = (unsigned char*)password;
98 auth_identity.PasswordLength = strlen(auth_identity.Password);
101 ret = AcquireCredentialsHandle( NULL,
102 (SEC_CHAR *)ctx->mech,
103 SECPKG_CRED_OUTBOUND,
104 NULL,
105 (context->sso || !username) ? NULL : &auth_identity,
106 NULL,
107 NULL,
108 cred_handle,
109 &expiry
112 if (ret != SEC_E_OK) {
113 sip_sec_sspi_print_error("sip_sec_acquire_cred__sspi: AcquireCredentialsHandle", ret);
114 ctx->cred_sspi = NULL;
115 return SIP_SEC_E_INTERNAL_ERROR;
116 } else {
117 ctx->cred_sspi = cred_handle;
118 return SIP_SEC_E_OK;
122 static sip_uint32
123 sip_sec_init_sec_context__sspi(SipSecContext context,
124 SipSecBuffer in_buff,
125 SipSecBuffer *out_buff,
126 const char *service_name)
128 TimeStamp expiry;
129 SecBufferDesc input_desc, output_desc;
130 SecBuffer in_token, out_token;
131 SECURITY_STATUS ret;
132 ULONG req_flags;
133 ULONG ret_flags;
134 context_sspi ctx = (context_sspi)context;
135 CtxtHandle* out_context = malloc(sizeof(CtxtHandle));
137 purple_debug_info("sipe", "sip_sec_init_sec_context__sspi: in use\n");
139 input_desc.cBuffers = 1;
140 input_desc.pBuffers = &in_token;
141 input_desc.ulVersion = SECBUFFER_VERSION;
143 /* input token */
144 in_token.BufferType = SECBUFFER_TOKEN;
145 in_token.cbBuffer = in_buff.length;
146 in_token.pvBuffer = in_buff.value;
148 output_desc.cBuffers = 1;
149 output_desc.pBuffers = &out_token;
150 output_desc.ulVersion = SECBUFFER_VERSION;
152 /* to hold output token */
153 out_token.BufferType = SECBUFFER_TOKEN;
154 out_token.cbBuffer = 0;
155 out_token.pvBuffer = NULL;
157 req_flags = (ISC_REQ_ALLOCATE_MEMORY |
158 ISC_REQ_INTEGRITY |
159 ISC_REQ_IDENTIFY);
161 if (!strcmp(ctx->mech, SSPI_MECH_NTLM)) {
162 req_flags |= (ISC_REQ_DATAGRAM);
165 ret = InitializeSecurityContext(ctx->cred_sspi,
166 ctx->ctx_sspi,
167 (SEC_CHAR *)service_name,
168 req_flags,
170 SECURITY_NATIVE_DREP,
171 &input_desc,
173 out_context,
174 &output_desc,
175 &ret_flags,
176 &expiry
179 if (ret != SEC_E_OK && ret != SEC_I_CONTINUE_NEEDED) {
180 sip_sec_destroy_sspi_context(ctx);
181 sip_sec_sspi_print_error("sip_sec_init_sec_context__sspi: InitializeSecurityContext", ret);
182 return SIP_SEC_E_INTERNAL_ERROR;
185 out_buff->length = out_token.cbBuffer;
186 out_buff->value = NULL;
187 if (out_token.cbBuffer) {
188 out_buff->value = g_malloc0(out_token.cbBuffer);
189 memmove(out_buff->value, out_token.pvBuffer, out_token.cbBuffer);
190 FreeContextBuffer(out_token.pvBuffer);
193 ctx->ctx_sspi = out_context;
194 if (!strcmp(ctx->mech, SSPI_MECH_KERBEROS)) {
195 context->expires = sip_sec_get_interval_from_now_sec(expiry);
198 if (ret == SEC_I_CONTINUE_NEEDED) {
199 return SIP_SEC_I_CONTINUE_NEEDED;
200 } else {
201 return SIP_SEC_E_OK;
205 static void
206 sip_sec_destroy_sec_context__sspi(SipSecContext context)
208 sip_sec_destroy_sspi_context((context_sspi)context);
209 g_free(context);
213 * @param message a NULL terminated string to sign
216 static sip_uint32
217 sip_sec_make_signature__sspi(SipSecContext context,
218 const char *message,
219 SipSecBuffer *signature)
221 SecBufferDesc buffs_desc;
222 SecBuffer buffs[2];
223 SECURITY_STATUS ret;
224 SecPkgContext_Sizes context_sizes;
225 unsigned char *signature_buff;
226 size_t signature_buff_length;
227 context_sspi ctx = (context_sspi) context;
229 ret = QueryContextAttributes(ctx->ctx_sspi,
230 SECPKG_ATTR_SIZES,
231 &context_sizes);
233 if (ret != SEC_E_OK) {
234 sip_sec_sspi_print_error("sip_sec_make_signature__sspi: QueryContextAttributes", ret);
235 return SIP_SEC_E_INTERNAL_ERROR;
238 signature_buff_length = context_sizes.cbMaxSignature;
239 signature_buff = malloc(signature_buff_length);
241 buffs_desc.cBuffers = 2;
242 buffs_desc.pBuffers = buffs;
243 buffs_desc.ulVersion = SECBUFFER_VERSION;
245 /* message to sign */
246 buffs[0].BufferType = SECBUFFER_DATA;
247 buffs[0].cbBuffer = strlen(message);
248 buffs[0].pvBuffer = (PVOID)message;
250 /* to hold signature */
251 buffs[1].BufferType = SECBUFFER_TOKEN;
252 buffs[1].cbBuffer = signature_buff_length;
253 buffs[1].pvBuffer = signature_buff;
255 ret = MakeSignature(ctx->ctx_sspi,
256 (ULONG)0,
257 &buffs_desc,
258 100);
259 if (ret != SEC_E_OK) {
260 sip_sec_sspi_print_error("sip_sec_make_signature__sspi: MakeSignature", ret);
261 free(signature_buff);
262 return SIP_SEC_E_INTERNAL_ERROR;
265 signature->value = signature_buff;
266 signature->length = buffs[1].cbBuffer;
268 return SIP_SEC_E_OK;
272 * @param message a NULL terminated string to check signature of
273 * @return SIP_SEC_E_OK on success
275 static sip_uint32
276 sip_sec_verify_signature__sspi(SipSecContext context,
277 const char *message,
278 SipSecBuffer signature)
280 SecBufferDesc buffs_desc;
281 SecBuffer buffs[2];
282 SECURITY_STATUS ret;
284 buffs_desc.cBuffers = 2;
285 buffs_desc.pBuffers = buffs;
286 buffs_desc.ulVersion = SECBUFFER_VERSION;
288 /* message to sign */
289 buffs[0].BufferType = SECBUFFER_DATA;
290 buffs[0].cbBuffer = strlen(message);
291 buffs[0].pvBuffer = (PVOID)message;
293 /* signature to check */
294 buffs[1].BufferType = SECBUFFER_TOKEN;
295 buffs[1].cbBuffer = signature.length;
296 buffs[1].pvBuffer = signature.value;
298 ret = VerifySignature(((context_sspi)context)->ctx_sspi,
299 &buffs_desc,
303 if (ret != SEC_E_OK) {
304 sip_sec_sspi_print_error("sip_sec_verify_signature__sspi: VerifySignature", ret);
305 return SIP_SEC_E_INTERNAL_ERROR;
308 return SIP_SEC_E_OK;
311 SipSecContext
312 sip_sec_create_context__sspi(SipSecAuthType type)
314 context_sspi context = g_malloc0(sizeof(struct _context_sspi));
315 if (!context) return(NULL);
317 context->common.acquire_cred_func = sip_sec_acquire_cred__sspi;
318 context->common.init_context_func = sip_sec_init_sec_context__sspi;
319 context->common.destroy_context_func = sip_sec_destroy_sec_context__sspi;
320 context->common.make_signature_func = sip_sec_make_signature__sspi;
321 context->common.verify_signature_func = sip_sec_verify_signature__sspi;
322 context->mech = (type == AUTH_TYPE_NTLM) ? SSPI_MECH_NTLM : SSPI_MECH_KERBEROS;
324 return((SipSecContext) context);
327 /* Utility Functions */
329 /**
330 * Returns interval in seconds from now till provided value
332 static int
333 sip_sec_get_interval_from_now_sec(TimeStamp timestamp)
335 SYSTEMTIME stNow;
336 FILETIME ftNow;
337 ULARGE_INTEGER uliNow, uliTo;
339 GetLocalTime(&stNow);
340 SystemTimeToFileTime(&stNow, &ftNow);
342 uliNow.LowPart = ftNow.dwLowDateTime;
343 uliNow.HighPart = ftNow.dwHighDateTime;
345 uliTo.LowPart = timestamp.LowPart;
346 uliTo.HighPart = timestamp.HighPart;
348 return (int)((uliTo.QuadPart - uliNow.QuadPart)/10/1000/1000);
351 void
352 sip_sec_sspi_print_error(const char *func,
353 SECURITY_STATUS ret)
355 char *error_message;
356 static char *buff;
357 int buff_length;
359 buff_length = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
360 FORMAT_MESSAGE_ALLOCATE_BUFFER |
361 FORMAT_MESSAGE_IGNORE_INSERTS,
363 ret,
365 (LPTSTR)&buff,
366 16384,
368 error_message = g_strndup(buff, buff_length);
369 LocalFree(buff);
371 printf("SSPI ERROR [%d] in %s: %s", (int)ret, func, error_message);
372 g_free(error_message);
377 Local Variables:
378 mode: c
379 c-file-style: "bsd"
380 indent-tabs-mode: t
381 tab-width: 8
382 End: