6 * Copyright (C) 2010-2013 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
31 #include <gssapi/gssapi.h>
32 #ifdef HAVE_GSSAPI_PASSWORD_SUPPORT
33 #include <gssapi/gssapi_ext.h>
35 #include <gssapi/gssapi_krb5.h>
37 #include "sipe-common.h"
39 #include "sip-sec-mech.h"
40 #include "sip-sec-krb5.h"
41 #include "sipe-backend.h"
42 #include "sipe-utils.h"
44 /* Security context for Kerberos */
45 typedef struct _context_krb5
{
46 struct sip_sec_context common
;
47 gss_cred_id_t cred_krb5
;
48 gss_ctx_id_t ctx_krb5
;
51 static void sip_sec_krb5_print_gss_error0(char *func
,
56 OM_uint32 message_context
= 0;
57 gss_buffer_desc status_string
;
60 gss_display_status(&minor
,
67 SIPE_DEBUG_ERROR("sip_sec_krb5: GSSAPI error in %s (%s): %s",
69 (type
== GSS_C_GSS_CODE
? "GSS" : "Mech"),
70 (gchar
*) status_string
.value
);
71 gss_release_buffer(&minor
, &status_string
);
72 } while (message_context
!= 0);
75 /* Prints out errors of GSSAPI function invocation */
76 static void sip_sec_krb5_print_gss_error(char *func
,
80 sip_sec_krb5_print_gss_error0(func
, ret
, GSS_C_GSS_CODE
);
81 sip_sec_krb5_print_gss_error0(func
, minor
, GSS_C_MECH_CODE
);
84 /* sip-sec-mech.h API implementation for Kerberos/GSSAPI */
87 sip_sec_acquire_cred__krb5(SipSecContext context
,
89 const gchar
*username
,
90 const gchar
*password
)
92 SIPE_DEBUG_INFO_NOFORMAT("sip_sec_acquire_cred__krb5: started");
94 /* With SSO we use the default credentials */
95 if ((context
->flags
& SIP_SEC_FLAG_COMMON_SSO
) == 0) {
96 #ifdef HAVE_GSSAPI_PASSWORD_SUPPORT
99 OM_uint32 minor
, minor_ignore
;
100 gss_cred_id_t credentials
;
101 gss_buffer_desc input_name_buffer
;
102 gss_name_t user_name
;
104 /* Without SSO we need user name and password */
105 if (!username
|| !password
) {
106 SIPE_DEBUG_ERROR_NOFORMAT("sip_sec_acquire_cred__krb5: no valid authentication information provided");
110 /* Construct user name to acquire credentials for */
111 if (!is_empty(domain
)) {
112 /* User specified a domain */
113 gchar
*realm
= g_ascii_strup(domain
, -1);
115 username_new
= g_strdup_printf("%s@%s",
120 } else if (strchr(username
, '@')) {
121 /* No domain, username matches XXX@YYY */
122 gchar
**user_realm
= g_strsplit(username
, "@", 2);
123 gchar
*realm
= g_ascii_strup(user_realm
[1], -1);
126 * We should escape the "@" to generate a enterprise
127 * principal, i.e. XXX\@YYY
129 * But krb5 libraries currently don't support this:
131 * http://krbdev.mit.edu/rt/Ticket/Display.html?id=7729
133 * username_new = g_strdup_printf("%s\\@%s",
135 username_new
= g_strdup_printf("%s@%s",
139 g_strfreev(user_realm
);
141 /* Otherwise use username as is */
142 username_new
= g_strdup(username
);
144 SIPE_DEBUG_INFO("sip_sec_acquire_cred__krb5: username '%s'",
147 /* Import user name into GSS format */
148 input_name_buffer
.value
= (void *) username_new
;
149 input_name_buffer
.length
= strlen(username_new
) + 1;
151 ret
= gss_import_name(&minor
,
153 (gss_OID
) GSS_C_NT_USER_NAME
,
155 g_free(username_new
);
157 if (GSS_ERROR(ret
)) {
158 sip_sec_krb5_print_gss_error("gss_import_name", ret
, minor
);
159 SIPE_DEBUG_ERROR("sip_sec_acquire_cred__krb5: failed to construct user name (ret=%d)", (int)ret
);
163 /* Acquire user credentials with password */
164 input_name_buffer
.value
= (void *) password
;
165 input_name_buffer
.length
= strlen(password
) + 1;
166 ret
= gss_acquire_cred_with_password(&minor
,
175 gss_release_name(&minor_ignore
, &user_name
);
177 if (GSS_ERROR(ret
)) {
178 sip_sec_krb5_print_gss_error("gss_acquire_cred_with_password", ret
, minor
);
179 SIPE_DEBUG_ERROR("sip_sec_acquire_cred__krb5: failed to acquire credentials (ret=%d)", (int)ret
);
182 ((context_krb5
) context
)->cred_krb5
= credentials
;
187 * non-SSO support requires gss_acquire_cred_with_password()
188 * which is not available on older GSSAPI releases.
190 (void) domain
; /* keep compiler happy */
191 (void) username
; /* keep compiler happy */
192 (void) password
; /* keep compiler happy */
193 SIPE_DEBUG_ERROR_NOFORMAT("sip_sec_acquire_cred__krb5: non-SSO mode not supported");
202 sip_sec_init_sec_context__krb5(SipSecContext context
,
203 SipSecBuffer in_buff
,
204 SipSecBuffer
*out_buff
,
205 const gchar
*service_name
)
207 context_krb5 ctx
= (context_krb5
) context
;
209 OM_uint32 minor
, minor_ignore
;
211 gss_buffer_desc input_token
;
212 gss_buffer_desc output_token
;
213 gss_name_t target_name
;
215 SIPE_DEBUG_INFO_NOFORMAT("sip_sec_init_sec_context__krb5: started");
218 * If authentication was already completed, then this mean a new
219 * authentication handshake has started on the existing connection.
220 * We must throw away the old context, because we need a new one.
222 if ((context
->flags
& SIP_SEC_FLAG_COMMON_READY
) &&
223 (ctx
->ctx_krb5
!= GSS_C_NO_CONTEXT
)) {
224 SIPE_DEBUG_INFO_NOFORMAT("sip_sec_init_sec_context__krb5: dropping old context");
225 ret
= gss_delete_sec_context(&minor
,
228 if (GSS_ERROR(ret
)) {
229 sip_sec_krb5_print_gss_error("gss_delete_sec_context", ret
, minor
);
230 SIPE_DEBUG_ERROR("sip_sec_init_sec_context__krb5: failed to delete security context (ret=%d)", (int)ret
);
232 ctx
->ctx_krb5
= GSS_C_NO_CONTEXT
;
233 context
->flags
&= ~SIP_SEC_FLAG_COMMON_READY
;
236 /* Import service name to GSS */
237 input_token
.value
= (void *) service_name
;
238 input_token
.length
= strlen(service_name
) + 1;
240 ret
= gss_import_name(&minor
,
242 (gss_OID
) GSS_KRB5_NT_PRINCIPAL_NAME
,
244 if (GSS_ERROR(ret
)) {
245 sip_sec_krb5_print_gss_error("gss_import_name", ret
, minor
);
246 SIPE_DEBUG_ERROR("sip_sec_init_sec_context__krb5: failed to construct target name (ret=%d)", (int)ret
);
251 input_token
.length
= in_buff
.length
;
252 input_token
.value
= in_buff
.value
;
254 output_token
.length
= 0;
255 output_token
.value
= NULL
;
257 ret
= gss_init_sec_context(&minor
,
261 (gss_OID
) gss_mech_krb5
,
264 GSS_C_NO_CHANNEL_BINDINGS
,
270 gss_release_name(&minor_ignore
, &target_name
);
272 if (GSS_ERROR(ret
)) {
273 gss_release_buffer(&minor_ignore
, &output_token
);
274 sip_sec_krb5_print_gss_error("gss_init_sec_context", ret
, minor
);
275 SIPE_DEBUG_ERROR("sip_sec_init_sec_context__krb5: failed to initialize context (ret=%d)", (int)ret
);
279 out_buff
->length
= output_token
.length
;
280 out_buff
->value
= g_memdup(output_token
.value
, output_token
.length
);
281 gss_release_buffer(&minor_ignore
, &output_token
);
283 context
->expires
= (int)expiry
;
285 /* Authentication is completed */
286 context
->flags
|= SIP_SEC_FLAG_COMMON_READY
;
292 * @param message a NULL terminated string to sign
295 sip_sec_make_signature__krb5(SipSecContext context
,
296 const gchar
*message
,
297 SipSecBuffer
*signature
)
301 gss_buffer_desc input_message
;
302 gss_buffer_desc output_token
;
304 input_message
.value
= (void *)message
;
305 input_message
.length
= strlen(input_message
.value
);
307 ret
= gss_get_mic(&minor
,
308 ((context_krb5
)context
)->ctx_krb5
,
313 if (GSS_ERROR(ret
)) {
314 sip_sec_krb5_print_gss_error("gss_get_mic", ret
, minor
);
315 SIPE_DEBUG_ERROR("sip_sec_make_signature__krb5: failed to make signature (ret=%d)", (int)ret
);
318 signature
->length
= output_token
.length
;
319 signature
->value
= g_memdup(output_token
.value
,
320 output_token
.length
);
321 gss_release_buffer(&minor
, &output_token
);
327 * @param message a NULL terminated string to check signature of
330 sip_sec_verify_signature__krb5(SipSecContext context
,
331 const gchar
*message
,
332 SipSecBuffer signature
)
336 gss_buffer_desc input_message
;
337 gss_buffer_desc input_token
;
339 input_message
.value
= (void *)message
;
340 input_message
.length
= strlen(input_message
.value
);
342 input_token
.value
= signature
.value
;
343 input_token
.length
= signature
.length
;
345 ret
= gss_verify_mic(&minor
,
346 ((context_krb5
)context
)->ctx_krb5
,
351 if (GSS_ERROR(ret
)) {
352 sip_sec_krb5_print_gss_error("gss_verify_mic", ret
, minor
);
353 SIPE_DEBUG_ERROR("sip_sec_verify_signature__krb5: failed to make signature (ret=%d)", (int)ret
);
361 sip_sec_destroy_sec_context__krb5(SipSecContext context
)
363 context_krb5 ctx
= (context_krb5
) context
;
367 if (ctx
->ctx_krb5
!= GSS_C_NO_CONTEXT
) {
368 ret
= gss_delete_sec_context(&minor
, &(ctx
->ctx_krb5
), GSS_C_NO_BUFFER
);
369 if (GSS_ERROR(ret
)) {
370 sip_sec_krb5_print_gss_error("gss_delete_sec_context", ret
, minor
);
371 SIPE_DEBUG_ERROR("sip_sec_destroy_sec_context__krb5: failed to delete security context (ret=%d)", (int)ret
);
373 ctx
->ctx_krb5
= GSS_C_NO_CONTEXT
;
376 if (ctx
->cred_krb5
!= GSS_C_NO_CREDENTIAL
) {
377 ret
= gss_release_cred(&minor
, &(ctx
->cred_krb5
));
378 if (GSS_ERROR(ret
)) {
379 sip_sec_krb5_print_gss_error("gss_release_cred", ret
, minor
);
380 SIPE_DEBUG_ERROR("sip_sec_destroy_sec_context__krb5: failed to release credentials (ret=%d)", (int)ret
);
382 ctx
->cred_krb5
= GSS_C_NO_CREDENTIAL
;
389 sip_sec_context_name__krb5(SIPE_UNUSED_PARAMETER SipSecContext context
)
395 sip_sec_create_context__krb5(SIPE_UNUSED_PARAMETER guint type
)
397 context_krb5 context
= g_malloc0(sizeof(struct _context_krb5
));
398 if (!context
) return(NULL
);
400 context
->common
.acquire_cred_func
= sip_sec_acquire_cred__krb5
;
401 context
->common
.init_context_func
= sip_sec_init_sec_context__krb5
;
402 context
->common
.destroy_context_func
= sip_sec_destroy_sec_context__krb5
;
403 context
->common
.make_signature_func
= sip_sec_make_signature__krb5
;
404 context
->common
.verify_signature_func
= sip_sec_verify_signature__krb5
;
405 context
->common
.context_name_func
= sip_sec_context_name__krb5
;
407 context
->cred_krb5
= GSS_C_NO_CREDENTIAL
;
408 context
->ctx_krb5
= GSS_C_NO_CONTEXT
;
410 return((SipSecContext
) context
);
413 gboolean
sip_sec_password__krb5(void)
415 /* Kerberos supports Single-Sign On */