security: simplify sip_sec_context_name__gssapi()
[siplcs.git] / src / core / sip-sec-gssapi.c
blob3505c79505421e94cab8a2430d4aeaf4dc4d4146
1 /**
2 * @file sip-sec-gssapi.c
4 * pidgin-sipe
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
25 * This module implements sip-sec authentication API using GSSAPI.
27 * It can be compiled in two different modes:
29 * - Kerberos-only: NTLM & SPNEGO are using SIPE internal implementation
30 * [HAVE_GSSAPI_ONLY is not defined]
32 * - pure GSSAPI: this modules handles Kerberos, NTLM & SPNEGO
33 * [HAVE_GSSAPI_ONLY is defined]
36 #ifdef HAVE_CONFIG_H
37 #include "config.h"
38 #endif
40 #include <glib.h>
41 #include <string.h>
42 #include <gssapi/gssapi.h>
43 #ifdef HAVE_GSSAPI_PASSWORD_SUPPORT
44 #include <gssapi/gssapi_ext.h>
45 #endif
46 #include <gssapi/gssapi_krb5.h>
47 #ifdef HAVE_GSSAPI_ONLY
48 #include <gssapi/gssapi_ntlmssp.h>
49 #endif
51 #include "sipe-common.h"
52 #include "sip-sec.h"
53 #include "sip-sec-mech.h"
54 #include "sip-sec-gssapi.h"
55 #include "sipe-backend.h"
56 #include "sipe-core.h"
57 #include "sipe-utils.h"
59 /* Security context for Kerberos */
60 typedef struct _context_gssapi {
61 struct sip_sec_context common;
62 gss_cred_id_t cred_gssapi;
63 gss_ctx_id_t ctx_gssapi;
64 gss_name_t target_name;
65 } *context_gssapi;
67 #ifdef HAVE_GSSAPI_ONLY
68 static const gss_OID_desc gss_mech_ntlmssp = {
69 GSS_NTLMSSP_OID_LENGTH,
70 GSS_NTLMSSP_OID_STRING
73 static const gss_OID_desc gss_mech_spnego = {
75 "\x2b\x06\x01\x05\x05\x02"
79 * The SPNEGO implementation on older Microsoft IIS servers sends a
80 * non-conformant final empty token that is not accepted by the SPNEGO
81 * implementation in older MIT KRB5 releases:
83 * Base64-encoded DER: oRgwFqADCgEAoQsGCSqGSIb3EgECAqICBAA=
85 * Decoded ASN.1:
86 * 0:d=0 hl=2 l= 24 cons: cont [ 1 ]
87 * 2:d=1 hl=2 l= 22 cons: SEQUENCE
88 * 4:d=2 hl=2 l= 3 cons: cont [ 0 ]
89 * 6:d=3 hl=2 l= 1 prim: ENUMERATED :00
90 * 9:d=2 hl=2 l= 11 cons: cont [ 1 ]
91 * 11:d=3 hl=2 l= 9 prim: OBJECT :1.2.840.113554.1.2.2
92 * 22:d=2 hl=2 l= 2 cons: cont [ 2 ] | this empty element is not
93 * 24:d=3 hl=2 l= 0 prim: OCTET STRING | correct according to spec
95 * We can circumvent this problem by setting GSS_C_MUTUAL_FLAG which causes
96 * the server to send a non-empty final token. We set the following flag to
97 * TRUE after the first time gss_init_sec_context() returns with a
98 * "defective token" error.
100 static gboolean spnego_mutual_flag = FALSE;
101 #endif
103 #define SIP_SEC_FLAG_GSSAPI_SIP_NTLM 0x00010000
104 #define SIP_SEC_FLAG_GSSAPI_NEGOTIATE_FALLBACK 0x00020000
106 static void sip_sec_gssapi_print_gss_error0(char *func,
107 OM_uint32 status,
108 int type)
110 OM_uint32 minor;
111 OM_uint32 message_context = 0;
112 gss_buffer_desc status_string;
114 do {
115 gss_display_status(&minor,
116 status,
117 type,
118 GSS_C_NO_OID,
119 &message_context,
120 &status_string);
122 SIPE_DEBUG_ERROR("sip_sec_gssapi: GSSAPI error in %s (%s): %s",
123 func,
124 (type == GSS_C_GSS_CODE ? "GSS" : "Mech"),
125 (gchar *) status_string.value);
126 gss_release_buffer(&minor, &status_string);
127 } while (message_context != 0);
130 /* Prints out errors of GSSAPI function invocation */
131 static void sip_sec_gssapi_print_gss_error(char *func,
132 OM_uint32 ret,
133 OM_uint32 minor)
135 sip_sec_gssapi_print_gss_error0(func, ret, GSS_C_GSS_CODE);
136 sip_sec_gssapi_print_gss_error0(func, minor, GSS_C_MECH_CODE);
139 #if defined(HAVE_GSSAPI_PASSWORD_SUPPORT) || defined(HAVE_GSSAPI_ONLY)
140 /* NOTE: releases "set" on error */
141 static gboolean add_mech(gss_OID_set set,
142 gss_OID mech,
143 const gchar *name)
145 OM_uint32 ret;
146 OM_uint32 minor;
148 ret = gss_add_oid_set_member(&minor, mech, &set);
149 if (GSS_ERROR(ret)) {
150 sip_sec_gssapi_print_gss_error("gss_add_oid_set_member", ret, minor);
151 SIPE_DEBUG_ERROR("add_mech: can't add %s to mech set (ret=%d)", name, (int)ret);
152 gss_release_oid_set(&minor, &set);
153 return(FALSE);
155 SIPE_DEBUG_INFO("add_mech: added %s to mech set", name);
157 return(TRUE);
160 static gss_OID_set create_mechs_set(guint type)
162 OM_uint32 ret;
163 OM_uint32 minor;
164 gss_OID_set set = GSS_C_NO_OID_SET;
165 gss_OID mech_oid;
166 const gchar *name;
168 ret = gss_create_empty_oid_set(&minor, &set);
169 if (GSS_ERROR(ret)) {
170 sip_sec_gssapi_print_gss_error("gss_create_empty_oid_set", ret, minor);
171 SIPE_DEBUG_ERROR("create_mechs_set: can't create mech set (ret=%d)", (int)ret);
172 return(GSS_C_NO_OID_SET);
175 #ifdef HAVE_GSSAPI_ONLY
176 switch (type) {
177 case SIPE_AUTHENTICATION_TYPE_NTLM:
178 mech_oid = (gss_OID) &gss_mech_ntlmssp;
179 name = "NTLM";
180 break;
182 case SIPE_AUTHENTICATION_TYPE_KERBEROS:
183 #else
184 (void) type; /* keep compiler happy */
185 #endif
186 mech_oid = (gss_OID) gss_mech_krb5;
187 name = "Kerberos";
188 #ifdef HAVE_GSSAPI_ONLY
189 break;
191 case SIPE_AUTHENTICATION_TYPE_NEGOTIATE:
192 mech_oid = (gss_OID) &gss_mech_spnego;
193 name = "SPNEGO";
194 break;
196 default:
197 SIPE_DEBUG_ERROR("create_mechs_set: invoked with invalid type %d",
198 type);
199 gss_release_oid_set(&minor, &set);
200 return(GSS_C_NO_OID_SET);
201 break;
203 #endif
205 return(add_mech(set, mech_oid, name) ? set : GSS_C_NO_OID_SET);
207 #endif
209 #ifdef HAVE_GSSAPI_ONLY
210 static gss_OID_set create_neg_mechs_set(void)
212 OM_uint32 ret;
213 OM_uint32 minor;
214 gss_OID_set set = GSS_C_NO_OID_SET;
216 ret = gss_create_empty_oid_set(&minor, &set);
217 if (GSS_ERROR(ret)) {
218 sip_sec_gssapi_print_gss_error("gss_create_empty_oid_set", ret, minor);
219 SIPE_DEBUG_ERROR("create_neg_mechs_set: can't create mech set (ret=%d)", (int)ret);
220 return(GSS_C_NO_OID_SET);
223 return((add_mech(set, (gss_OID) gss_mech_krb5, "Kerberos") &&
224 add_mech(set, (gss_OID) &gss_mech_ntlmssp, "NTLM")) ?
225 set : GSS_C_NO_OID_SET);
228 static gboolean gssntlm_reset_mic_sequence(context_gssapi context)
230 OM_uint32 ret;
231 OM_uint32 minor;
232 gss_buffer_desc value;
233 guint sequence = 100;
235 static const gss_OID_desc set_sequence_num_oid = {
236 GSS_NTLMSSP_SET_SEQ_NUM_OID_LENGTH,
237 GSS_NTLMSSP_SET_SEQ_NUM_OID_STRING
240 value.length = sizeof(sequence);
241 value.value = &sequence;
243 ret = gss_set_sec_context_option(&minor,
244 &context->ctx_gssapi,
245 (gss_OID_desc *) &set_sequence_num_oid,
246 &value);
247 if (GSS_ERROR(ret)) {
248 sip_sec_gssapi_print_gss_error("gss_set_sec_context_option", ret, minor);
249 SIPE_DEBUG_ERROR("gssntlm_reset_mic_sequence: failed to reset MIC sequence number (ret=%d)", (int)ret);
250 return(FALSE);
253 return(TRUE);
255 #endif
257 static void drop_gssapi_context(SipSecContext context)
259 context_gssapi ctx = (context_gssapi) context;
260 OM_uint32 ret;
261 OM_uint32 minor;
263 ret = gss_delete_sec_context(&minor,
264 &(ctx->ctx_gssapi),
265 GSS_C_NO_BUFFER);
266 if (GSS_ERROR(ret)) {
267 sip_sec_gssapi_print_gss_error("gss_delete_sec_context", ret, minor);
268 SIPE_DEBUG_ERROR("drop_gssapi_context: failed to delete security context (ret=%d)", (int)ret);
270 ctx->ctx_gssapi = GSS_C_NO_CONTEXT;
271 context->flags &= ~SIP_SEC_FLAG_COMMON_READY;
274 /* sip-sec-mech.h API implementation for Kerberos/GSSAPI */
276 static gboolean
277 sip_sec_acquire_cred__gssapi(SipSecContext context,
278 const gchar *domain,
279 const gchar *username,
280 const gchar *password)
282 context_gssapi ctx = (context_gssapi) context;
284 SIPE_DEBUG_INFO_NOFORMAT("sip_sec_acquire_cred__gssapi: started");
286 /* this is the first time we are allowed to set private flags */
287 if (((context->flags & SIP_SEC_FLAG_COMMON_HTTP) == 0) &&
288 (context->type == SIPE_AUTHENTICATION_TYPE_NTLM))
289 context->flags |= SIP_SEC_FLAG_GSSAPI_SIP_NTLM;
291 /* With SSO we use the default credentials */
292 if ((context->flags & SIP_SEC_FLAG_COMMON_SSO) == 0) {
293 #ifdef HAVE_GSSAPI_PASSWORD_SUPPORT
294 gchar *username_new;
295 OM_uint32 ret;
296 OM_uint32 minor, minor_ignore;
297 gss_OID_set mechs_set;
298 gss_cred_id_t credentials;
299 gss_buffer_desc input_name_buffer;
300 gss_name_t user_name;
302 /* Without SSO we need user name and password */
303 if (!username || !password) {
304 SIPE_DEBUG_ERROR_NOFORMAT("sip_sec_acquire_cred__gssapi: no valid authentication information provided");
305 return(FALSE);
308 mechs_set = create_mechs_set(context->type);
309 if (mechs_set == GSS_C_NO_OID_SET)
310 return(FALSE);
312 /* Construct user name to acquire credentials for */
313 if (!is_empty(domain)) {
314 /* User specified a domain */
315 gchar *realm = g_ascii_strup(domain, -1);
317 username_new = g_strdup_printf("%s@%s",
318 username,
319 realm);
320 g_free(realm);
322 } else if (strchr(username, '@')) {
323 /* No domain, username matches XXX@YYY */
324 gchar **user_realm = g_strsplit(username, "@", 2);
325 gchar *realm = g_ascii_strup(user_realm[1], -1);
328 * We should escape the "@" to generate a enterprise
329 * principal, i.e. XXX\@YYY
331 * But krb5 libraries currently don't support this:
333 * http://krbdev.mit.edu/rt/Ticket/Display.html?id=7729
335 * username_new = g_strdup_printf("%s\\@%s",
337 username_new = g_strdup_printf("%s@%s",
338 user_realm[0],
339 realm);
340 g_free(realm);
341 g_strfreev(user_realm);
342 } else {
343 /* Otherwise use username as is */
344 username_new = g_strdup(username);
346 SIPE_DEBUG_INFO("sip_sec_acquire_cred__gssapi: username '%s'",
347 username_new);
349 /* Import user name into GSS format */
350 input_name_buffer.value = (void *) username_new;
351 input_name_buffer.length = strlen(username_new) + 1;
353 ret = gss_import_name(&minor,
354 &input_name_buffer,
355 (gss_OID) GSS_C_NT_USER_NAME,
356 &user_name);
357 g_free(username_new);
359 if (GSS_ERROR(ret)) {
360 sip_sec_gssapi_print_gss_error("gss_import_name", ret, minor);
361 SIPE_DEBUG_ERROR("sip_sec_acquire_cred__gssapi: failed to construct user name (ret=%d)", (int)ret);
362 gss_release_oid_set(&minor, &mechs_set);
363 return(FALSE);
366 /* Acquire user credentials with password */
367 input_name_buffer.value = (void *) password;
368 input_name_buffer.length = strlen(password) + 1;
369 ret = gss_acquire_cred_with_password(&minor,
370 user_name,
371 &input_name_buffer,
372 GSS_C_INDEFINITE,
373 mechs_set,
374 GSS_C_INITIATE,
375 &credentials,
376 NULL,
377 NULL);
378 gss_release_name(&minor_ignore, &user_name);
379 gss_release_oid_set(&minor_ignore, &mechs_set);
381 if (GSS_ERROR(ret)) {
382 sip_sec_gssapi_print_gss_error("gss_acquire_cred_with_password", ret, minor);
383 SIPE_DEBUG_ERROR("sip_sec_acquire_cred__gssapi: failed to acquire credentials (ret=%d)", (int)ret);
384 return(FALSE);
387 ctx->cred_gssapi = credentials;
389 #else
391 * non-SSO support requires gss_acquire_cred_with_password()
392 * which is not available on older GSSAPI releases.
394 (void) domain; /* keep compiler happy */
395 (void) username; /* keep compiler happy */
396 (void) password; /* keep compiler happy */
397 (void) ctx; /* keep compiler happy */
398 SIPE_DEBUG_ERROR_NOFORMAT("sip_sec_acquire_cred__gssapi: non-SSO mode not supported");
399 return(FALSE);
400 #endif
402 #ifdef HAVE_GSSAPI_ONLY
403 else {
404 OM_uint32 ret;
405 OM_uint32 minor, minor_ignore;
406 gss_OID_set mechs_set;
407 gss_cred_id_t credentials;
409 mechs_set = create_mechs_set(context->type);
410 if (mechs_set == GSS_C_NO_OID_SET)
411 return(FALSE);
413 ret = gss_acquire_cred(&minor,
414 GSS_C_NO_NAME,
415 GSS_C_INDEFINITE,
416 mechs_set,
417 GSS_C_INITIATE,
418 &credentials,
419 NULL,
420 NULL);
421 gss_release_oid_set(&minor_ignore, &mechs_set);
423 if (GSS_ERROR(ret)) {
424 sip_sec_gssapi_print_gss_error("gss_acquire_cred", ret, minor);
425 SIPE_DEBUG_ERROR("sip_sec_acquire_cred__gssapi: failed to acquire credentials (ret=%d)", (int)ret);
426 return(FALSE);
429 ctx->cred_gssapi = credentials;
432 if (context->type == SIPE_AUTHENTICATION_TYPE_NEGOTIATE) {
433 OM_uint32 ret;
434 OM_uint32 minor, minor_ignore;
435 gss_OID_set mechs_set = create_neg_mechs_set();
437 if (mechs_set == GSS_C_NO_OID_SET)
438 return(FALSE);
440 ret = gss_set_neg_mechs(&minor,
441 ctx->cred_gssapi,
442 mechs_set);
443 gss_release_oid_set(&minor_ignore, &mechs_set);
445 if (GSS_ERROR(ret)) {
446 sip_sec_gssapi_print_gss_error("gss_set_neg_mechs", ret, minor);
447 SIPE_DEBUG_ERROR("sip_sec_acquire_cred__gssapi: failed to set negotiate mechanisms (ret=%d)", (int)ret);
448 return(FALSE);
451 #endif
453 return(TRUE);
456 static gboolean
457 sip_sec_init_sec_context__gssapi(SipSecContext context,
458 SipSecBuffer in_buff,
459 SipSecBuffer *out_buff,
460 const gchar *service_name)
462 context_gssapi ctx = (context_gssapi) context;
463 OM_uint32 ret;
464 OM_uint32 minor, minor_ignore;
465 OM_uint32 expiry;
466 gss_buffer_desc input_token;
467 gss_buffer_desc output_token;
468 #ifdef HAVE_GSSAPI_ONLY
469 gss_OID mech_oid;
470 OM_uint32 flags = GSS_C_INTEG_FLAG;
471 #endif
473 SIPE_DEBUG_INFO_NOFORMAT("sip_sec_init_sec_context__gssapi: started");
476 * If authentication was already completed, then this mean a new
477 * authentication handshake has started on the existing connection.
478 * We must throw away the old context, because we need a new one.
480 if ((context->flags & SIP_SEC_FLAG_COMMON_READY) &&
481 (ctx->ctx_gssapi != GSS_C_NO_CONTEXT)) {
482 SIPE_DEBUG_INFO_NOFORMAT("sip_sec_init_sec_context__gssapi: dropping old context");
483 drop_gssapi_context(context);
486 /* Import service name to GSS */
487 if (ctx->target_name == GSS_C_NO_NAME) {
488 gchar *hostbased_service_name = NULL;
489 gchar **type_service = g_strsplit(service_name, "/", 2);
491 if (type_service[1]) {
492 gchar *type_lower = g_ascii_strdown(type_service[0], -1);
493 hostbased_service_name = g_strdup_printf("%s@%s",
494 type_lower,
495 type_service[1]);
496 g_free(type_lower);
497 input_token.value = (void *) hostbased_service_name;
498 } else {
499 input_token.value = (void *) service_name;
501 g_strfreev(type_service);
503 input_token.length = strlen(input_token.value) + 1;
504 ret = gss_import_name(&minor,
505 &input_token,
506 (gss_OID) GSS_C_NT_HOSTBASED_SERVICE,
507 &(ctx->target_name));
508 g_free(hostbased_service_name);
510 if (GSS_ERROR(ret)) {
511 sip_sec_gssapi_print_gss_error("gss_import_name", ret, minor);
512 SIPE_DEBUG_ERROR("sip_sec_init_sec_context__gssapi: failed to construct target name (ret=%d)", (int)ret);
513 return(FALSE);
517 #ifdef HAVE_GSSAPI_ONLY
518 switch(context->type) {
519 case SIPE_AUTHENTICATION_TYPE_NTLM:
520 mech_oid = (gss_OID) &gss_mech_ntlmssp;
521 if (context->flags & SIP_SEC_FLAG_GSSAPI_SIP_NTLM)
522 flags |= GSS_C_DATAGRAM_FLAG;
523 break;
525 case SIPE_AUTHENTICATION_TYPE_KERBEROS:
526 mech_oid = (gss_OID) gss_mech_krb5;
527 break;
529 case SIPE_AUTHENTICATION_TYPE_NEGOTIATE:
531 * Some servers do not accept SPNEGO for Negotiate.
532 * If come back here with an existing security context
533 * and NULL input token we will fall back to NTLM
535 if (ctx->ctx_gssapi && (in_buff.value == NULL)) {
537 /* Only try this once */
538 if (context->flags & SIP_SEC_FLAG_GSSAPI_NEGOTIATE_FALLBACK) {
539 SIPE_DEBUG_ERROR_NOFORMAT("sip_sec_init_sec_context__gssapi: SPNEGO-to-NTLM fallback failed");
540 return(FALSE);
543 SIPE_DEBUG_INFO_NOFORMAT("sip_sec_init_sec_context__gssapi: SPNEGO failed. Falling back to NTLM");
544 drop_gssapi_context(context);
546 context->flags |= SIP_SEC_FLAG_GSSAPI_NEGOTIATE_FALLBACK;
549 if (context->flags & SIP_SEC_FLAG_GSSAPI_NEGOTIATE_FALLBACK) {
550 mech_oid = (gss_OID) &gss_mech_ntlmssp;
551 } else {
552 mech_oid = (gss_OID) &gss_mech_spnego;
553 if (spnego_mutual_flag)
554 flags |= GSS_C_MUTUAL_FLAG;
556 break;
558 default:
559 SIPE_DEBUG_ERROR("sip_sec_init_sec_context__gssapi: invoked for invalid type %d",
560 context->type);
561 return(FALSE);
563 #endif
565 /* Create context */
566 input_token.length = in_buff.length;
567 input_token.value = in_buff.value;
569 output_token.length = 0;
570 output_token.value = NULL;
572 ret = gss_init_sec_context(&minor,
573 ctx->cred_gssapi,
574 &(ctx->ctx_gssapi),
575 ctx->target_name,
576 #ifdef HAVE_GSSAPI_ONLY
577 mech_oid,
578 flags,
579 #else
580 (gss_OID) gss_mech_krb5,
581 GSS_C_INTEG_FLAG,
582 #endif
583 GSS_C_INDEFINITE,
584 GSS_C_NO_CHANNEL_BINDINGS,
585 &input_token,
586 NULL,
587 &output_token,
588 NULL,
589 &expiry);
591 if (GSS_ERROR(ret)) {
592 gss_release_buffer(&minor_ignore, &output_token);
593 sip_sec_gssapi_print_gss_error("gss_init_sec_context", ret, minor);
594 SIPE_DEBUG_ERROR("sip_sec_init_sec_context__gssapi: failed to initialize context (ret=%d)", (int)ret);
596 #ifdef HAVE_GSSAPI_ONLY
597 /* Enable workaround for SPNEGO (see above) */
598 if (ret == GSS_S_DEFECTIVE_TOKEN) {
599 SIPE_DEBUG_ERROR_NOFORMAT("sip_sec_init_sec_context__gssapi: enabling workaround for SPNEGO");
600 spnego_mutual_flag = TRUE;
602 #endif
604 return(FALSE);
607 out_buff->length = output_token.length;
608 if (out_buff->length)
609 out_buff->value = g_memdup(output_token.value, output_token.length);
610 else
611 /* Special case: empty token */
612 out_buff->value = (guint8 *) g_strdup("");
614 gss_release_buffer(&minor_ignore, &output_token);
616 context->expires = (int)expiry;
618 if (ret == GSS_S_COMPLETE) {
619 /* Authentication is completed */
620 context->flags |= SIP_SEC_FLAG_COMMON_READY;
622 #ifdef HAVE_GSSAPI_ONLY
623 if ((context->flags & SIP_SEC_FLAG_GSSAPI_SIP_NTLM) &&
624 !gssntlm_reset_mic_sequence(ctx))
625 return(FALSE);
626 #endif
629 return(TRUE);
633 * @param message a NULL terminated string to sign
635 static gboolean
636 sip_sec_make_signature__gssapi(SipSecContext context,
637 const gchar *message,
638 SipSecBuffer *signature)
640 OM_uint32 ret;
641 OM_uint32 minor;
642 gss_buffer_desc input_message;
643 gss_buffer_desc output_token;
645 input_message.value = (void *)message;
646 input_message.length = strlen(input_message.value);
648 ret = gss_get_mic(&minor,
649 ((context_gssapi)context)->ctx_gssapi,
650 GSS_C_QOP_DEFAULT,
651 &input_message,
652 &output_token);
654 if (GSS_ERROR(ret)) {
655 sip_sec_gssapi_print_gss_error("gss_get_mic", ret, minor);
656 SIPE_DEBUG_ERROR("sip_sec_make_signature__gssapi: failed to make signature (ret=%d)", (int)ret);
657 return FALSE;
658 } else {
659 signature->length = output_token.length;
660 signature->value = g_memdup(output_token.value,
661 output_token.length);
662 gss_release_buffer(&minor, &output_token);
663 return TRUE;
668 * @param message a NULL terminated string to check signature of
670 static gboolean
671 sip_sec_verify_signature__gssapi(SipSecContext context,
672 const gchar *message,
673 SipSecBuffer signature)
675 OM_uint32 ret;
676 OM_uint32 minor;
677 gss_buffer_desc input_message;
678 gss_buffer_desc input_token;
680 input_message.value = (void *)message;
681 input_message.length = strlen(input_message.value);
683 input_token.value = signature.value;
684 input_token.length = signature.length;
686 ret = gss_verify_mic(&minor,
687 ((context_gssapi)context)->ctx_gssapi,
688 &input_message,
689 &input_token,
690 NULL);
692 if (GSS_ERROR(ret)) {
693 sip_sec_gssapi_print_gss_error("gss_verify_mic", ret, minor);
694 SIPE_DEBUG_ERROR("sip_sec_verify_signature__gssapi: failed to verify signature (ret=%d)", (int)ret);
695 return FALSE;
696 } else {
697 return TRUE;
701 static void
702 sip_sec_destroy_sec_context__gssapi(SipSecContext context)
704 context_gssapi ctx = (context_gssapi) context;
705 OM_uint32 ret;
706 OM_uint32 minor;
708 if (ctx->ctx_gssapi != GSS_C_NO_CONTEXT)
709 drop_gssapi_context(context);
711 if (ctx->cred_gssapi != GSS_C_NO_CREDENTIAL) {
712 ret = gss_release_cred(&minor, &(ctx->cred_gssapi));
713 if (GSS_ERROR(ret)) {
714 sip_sec_gssapi_print_gss_error("gss_release_cred", ret, minor);
715 SIPE_DEBUG_ERROR("sip_sec_destroy_sec_context__gssapi: failed to release credentials (ret=%d)", (int)ret);
717 ctx->cred_gssapi = GSS_C_NO_CREDENTIAL;
720 if (ctx->target_name != GSS_C_NO_NAME) {
721 ret = gss_release_name(&minor, &(ctx->target_name));
722 if (GSS_ERROR(ret)) {
723 sip_sec_gssapi_print_gss_error("gss_release_name", ret, minor);
724 SIPE_DEBUG_ERROR("sip_sec_destroy_sec_context__gssapi: failed to release name (ret=%d)", (int)ret);
726 ctx->target_name = GSS_C_NO_NAME;
729 g_free(context);
732 static const gchar *
733 sip_sec_context_name__gssapi(SipSecContext context)
735 #ifdef HAVE_GSSAPI_ONLY
736 const gchar *name;
738 switch(context->type) {
739 case SIPE_AUTHENTICATION_TYPE_NTLM:
740 name = "NTLM";
741 break;
743 case SIPE_AUTHENTICATION_TYPE_KERBEROS:
744 name = "Kerberos";
745 break;
747 case SIPE_AUTHENTICATION_TYPE_NEGOTIATE:
748 if (context->flags & SIP_SEC_FLAG_GSSAPI_NEGOTIATE_FALLBACK)
749 name = "NTLM";
750 else
751 name = "Negotiate";
752 break;
754 default:
755 SIPE_DEBUG_ERROR("sip_sec_context_name__gssapi: invoked for invalid type %d",
756 context->type);
757 name = "";
758 break;
761 return(name);
763 #else
764 (void) context; /* keep compiler happy */
765 return("Kerberos");
766 #endif
769 SipSecContext
770 sip_sec_create_context__gssapi(SIPE_UNUSED_PARAMETER guint type)
772 context_gssapi context = g_malloc0(sizeof(struct _context_gssapi));
773 if (!context) return(NULL);
775 context->common.acquire_cred_func = sip_sec_acquire_cred__gssapi;
776 context->common.init_context_func = sip_sec_init_sec_context__gssapi;
777 context->common.destroy_context_func = sip_sec_destroy_sec_context__gssapi;
778 context->common.make_signature_func = sip_sec_make_signature__gssapi;
779 context->common.verify_signature_func = sip_sec_verify_signature__gssapi;
780 context->common.context_name_func = sip_sec_context_name__gssapi;
782 context->cred_gssapi = GSS_C_NO_CREDENTIAL;
783 context->ctx_gssapi = GSS_C_NO_CONTEXT;
784 context->target_name = GSS_C_NO_NAME;
786 return((SipSecContext) context);
789 gboolean sip_sec_password__gssapi(void)
791 /* Kerberos supports Single-Sign On */
792 return(FALSE);
796 Local Variables:
797 mode: c
798 c-file-style: "bsd"
799 indent-tabs-mode: t
800 tab-width: 8
801 End: