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
27 #include <gssapi/gssapi.h>
28 #include <gssapi/gssapi_krb5.h>
31 #include "sipe-common.h"
33 #include "sip-sec-mech.h"
34 #include "sip-sec-krb5.h"
35 #include "sipe-backend.h"
37 /* Security context for Kerberos */
38 typedef struct _context_krb5
{
39 struct sip_sec_context common
;
40 gss_cred_id_t cred_krb5
;
41 gss_ctx_id_t ctx_krb5
;
44 static void sip_sec_krb5_print_gss_error(char *func
, OM_uint32 ret
, OM_uint32 minor
);
47 sip_sec_krb5_obtain_tgt(const char *domain
,
49 const char *password
);
51 /* sip-sec-mech.h API implementation for Kerberos/GSS-API */
54 * Depending on Single Sign-On flag (sso),
55 * obtains existing credentials stored in credentials cash in case of Kerberos,
56 * or attemps to obtain TGT on its own first.
59 sip_sec_acquire_cred__krb5(SipSecContext context
,
66 gss_cred_id_t credentials
;
69 /* Do not use default credentials, obtain a new one and store it in cache */
70 sip_sec_krb5_obtain_tgt(domain
, username
, password
);
73 /* Acquire default user credentials */
74 ret
= gss_acquire_cred(&minor
,
84 sip_sec_krb5_print_gss_error("gss_acquire_cred", ret
, minor
);
85 SIPE_DEBUG_ERROR("sip_sec_acquire_cred__krb5: failed to acquire credentials (ret=%d)", (int)ret
);
86 return SIP_SEC_E_INTERNAL_ERROR
;
88 ((context_krb5
)context
)->cred_krb5
= credentials
;
94 sip_sec_init_sec_context__krb5(SipSecContext context
,
96 SipSecBuffer
*out_buff
,
97 const char *service_name
)
100 OM_uint32 minor
, minor_ignore
;
102 gss_buffer_desc input_token
;
103 gss_buffer_desc output_token
;
104 gss_buffer_desc input_name_buffer
;
105 gss_name_t target_name
;
106 context_krb5 ctx
= (context_krb5
) context
;
108 SIPE_DEBUG_INFO_NOFORMAT("sip_sec_init_sec_context__krb5: started");
110 /* Delete old context first */
111 if (ctx
->ctx_krb5
!= GSS_C_NO_CONTEXT
) {
112 ret
= gss_delete_sec_context(&minor
,
115 if (GSS_ERROR(ret
)) {
116 sip_sec_krb5_print_gss_error("gss_delete_sec_context", ret
, minor
);
117 SIPE_DEBUG_ERROR("sip_sec_init_sec_context__krb5: failed to delete security context (ret=%d)", (int)ret
);
119 ctx
->ctx_krb5
= GSS_C_NO_CONTEXT
;
122 input_name_buffer
.value
= (void *) service_name
;
123 input_name_buffer
.length
= strlen(service_name
) + 1;
125 ret
= gss_import_name(&minor
,
127 (gss_OID
) GSS_KRB5_NT_PRINCIPAL_NAME
,
129 if (GSS_ERROR(ret
)) {
130 sip_sec_krb5_print_gss_error("gss_import_name", ret
, minor
);
131 SIPE_DEBUG_ERROR("sip_sec_init_sec_context__krb5: failed to construct target name (ret=%d)", (int)ret
);
132 return SIP_SEC_E_INTERNAL_ERROR
;
135 input_token
.length
= in_buff
.length
;
136 input_token
.value
= in_buff
.value
;
138 output_token
.length
= 0;
139 output_token
.value
= NULL
;
141 ret
= gss_init_sec_context(&minor
,
148 GSS_C_NO_CHANNEL_BINDINGS
,
154 gss_release_name(&minor_ignore
, &target_name
);
156 if (GSS_ERROR(ret
)) {
157 gss_release_buffer(&minor_ignore
, &output_token
);
158 sip_sec_krb5_print_gss_error("gss_init_sec_context", ret
, minor
);
159 SIPE_DEBUG_ERROR("sip_sec_init_sec_context__krb5: failed to initialize context (ret=%d)", (int)ret
);
160 return SIP_SEC_E_INTERNAL_ERROR
;
163 out_buff
->length
= output_token
.length
;
164 out_buff
->value
= g_memdup(output_token
.value
, output_token
.length
);
165 gss_release_buffer(&minor_ignore
, &output_token
);
167 context
->expires
= (int)expiry
;
169 /* Authentication is completed */
170 context
->is_ready
= TRUE
;
176 * @param message a NULL terminated string to sign
179 sip_sec_make_signature__krb5(SipSecContext context
,
181 SipSecBuffer
*signature
)
185 gss_buffer_desc input_message
;
186 gss_buffer_desc output_token
;
188 input_message
.value
= (void *)message
;
189 input_message
.length
= strlen(input_message
.value
);
191 ret
= gss_get_mic(&minor
,
192 ((context_krb5
)context
)->ctx_krb5
,
197 if (GSS_ERROR(ret
)) {
198 sip_sec_krb5_print_gss_error("gss_get_mic", ret
, minor
);
199 SIPE_DEBUG_ERROR("sip_sec_make_signature__krb5: failed to make signature (ret=%d)", (int)ret
);
200 return SIP_SEC_E_INTERNAL_ERROR
;
202 signature
->length
= output_token
.length
;
203 signature
->value
= g_memdup(output_token
.value
,
204 output_token
.length
);
205 gss_release_buffer(&minor
, &output_token
);
211 * @param message a NULL terminated string to check signature of
214 sip_sec_verify_signature__krb5(SipSecContext context
,
216 SipSecBuffer signature
)
220 gss_buffer_desc input_message
;
221 gss_buffer_desc input_token
;
223 input_message
.value
= (void *)message
;
224 input_message
.length
= strlen(input_message
.value
);
226 input_token
.value
= signature
.value
;
227 input_token
.length
= signature
.length
;
229 ret
= gss_verify_mic(&minor
,
230 ((context_krb5
)context
)->ctx_krb5
,
235 if (GSS_ERROR(ret
)) {
236 sip_sec_krb5_print_gss_error("gss_verify_mic", ret
, minor
);
237 SIPE_DEBUG_ERROR("sip_sec_verify_signature__krb5: failed to make signature (ret=%d)", (int)ret
);
238 return SIP_SEC_E_INTERNAL_ERROR
;
245 sip_sec_destroy_sec_context__krb5(SipSecContext context
)
249 context_krb5 ctx
= (context_krb5
) context
;
251 if (ctx
->ctx_krb5
!= GSS_C_NO_CONTEXT
) {
252 ret
= gss_delete_sec_context(&minor
, &(ctx
->ctx_krb5
), GSS_C_NO_BUFFER
);
253 if (GSS_ERROR(ret
)) {
254 sip_sec_krb5_print_gss_error("gss_delete_sec_context", ret
, minor
);
255 SIPE_DEBUG_ERROR("sip_sec_destroy_sec_context__krb5: failed to delete security context (ret=%d)", (int)ret
);
259 if (ctx
->cred_krb5
) {
260 ret
= gss_release_cred(&minor
, &(ctx
->cred_krb5
));
261 if (GSS_ERROR(ret
)) {
262 sip_sec_krb5_print_gss_error("gss_release_cred", ret
, minor
);
263 SIPE_DEBUG_ERROR("sip_sec_destroy_sec_context__krb5: failed to release credentials (ret=%d)", (int)ret
);
271 sip_sec_create_context__krb5(SIPE_UNUSED_PARAMETER guint type
)
273 context_krb5 context
= g_malloc0(sizeof(struct _context_krb5
));
274 if (!context
) return(NULL
);
276 context
->common
.acquire_cred_func
= sip_sec_acquire_cred__krb5
;
277 context
->common
.init_context_func
= sip_sec_init_sec_context__krb5
;
278 context
->common
.destroy_context_func
= sip_sec_destroy_sec_context__krb5
;
279 context
->common
.make_signature_func
= sip_sec_make_signature__krb5
;
280 context
->common
.verify_signature_func
= sip_sec_verify_signature__krb5
;
282 context
->ctx_krb5
= GSS_C_NO_CONTEXT
;
284 return((SipSecContext
) context
);
287 gboolean
sip_sec_password__krb5(void)
289 /* Kerberos supports Single-Sign On */
294 sip_sec_krb5_print_gss_error0(char *func
,
299 OM_uint32 message_context
= 0;
300 gss_buffer_desc status_string
;
303 gss_display_status(&minor
,
310 SIPE_DEBUG_ERROR("sip_sec_krb5: GSS-API error in %s (%s): %s", func
, (type
== GSS_C_GSS_CODE
? "GSS" : "Mech"), (char *)status_string
.value
);
311 gss_release_buffer(&minor
, &status_string
);
312 } while (message_context
!= 0);
316 * Prints out errors of GSS-API function invocation
318 static void sip_sec_krb5_print_gss_error(char *func
, OM_uint32 ret
, OM_uint32 minor
)
320 sip_sec_krb5_print_gss_error0(func
, ret
, GSS_C_GSS_CODE
);
321 sip_sec_krb5_print_gss_error0(func
, minor
, GSS_C_MECH_CODE
);
325 * Prints out errors of Kerberos 5 function invocation
328 sip_sec_krb5_print_error(const char *func
,
329 krb5_context context
,
330 krb5_error_code ret
);
333 * Obtains Kerberos TGT and stores it in default credentials cache.
334 * Similar what kinit util would do.
335 * Can be checked with klist util.
337 * kinit would require the following name:
338 * alice@ATLANTA.LOCAL
339 * where 'alice' is a username and
340 * 'ATLANTA.LOCAL' is a realm (domain) .
343 sip_sec_krb5_obtain_tgt(const char *domain_in
,
344 const char *username_in
,
345 const char *password
)
347 krb5_context context
;
348 krb5_principal principal
= NULL
;
349 krb5_creds credentials
;
356 SIPE_DEBUG_INFO_NOFORMAT("sip_sec_krb5_obtain_tgt: started");
358 memset(&credentials
, 0, sizeof(krb5_creds
));
360 user_realm
= g_strsplit(username_in
, "@", 2);
361 if (user_realm
&& user_realm
[1]) {
362 /* "user@domain" -> use domain as realm */
363 realm
= g_ascii_strup(user_realm
[1], -1);
364 username
= g_strdup(user_realm
[0]);
366 /* use provided domain as realm */
367 realm
= g_ascii_strup(domain_in
? domain_in
: "", -1);
368 username
= g_strdup(username_in
);
370 g_strfreev(user_realm
);
373 if ((ret
= krb5_init_context(&context
))) {
374 sip_sec_krb5_print_error("krb5_init_context", context
, ret
);
377 if (!ret
&& (ret
= krb5_build_principal(context
, &principal
, strlen(realm
), realm
, username
, NULL
))) {
378 sip_sec_krb5_print_error("krb5_build_principal", context
, ret
);
383 if (!ret
&& (ret
= krb5_get_init_creds_password(context
, &credentials
, principal
, (char *)password
, NULL
, NULL
, 0, NULL
, NULL
))) {
384 sip_sec_krb5_print_error("krb5_get_init_creds_password", context
, ret
);
388 SIPE_DEBUG_INFO_NOFORMAT("sip_sec_krb5_obtain_tgt: new TGT obtained");
392 /* Store TGT in default credential cache */
393 if (!ret
&& (ret
= krb5_cc_default(context
, &ccdef
))) {
394 sip_sec_krb5_print_error("krb5_cc_default", context
, ret
);
397 if (!ret
&& (ret
= krb5_cc_initialize(context
, ccdef
, credentials
.client
))) {
398 sip_sec_krb5_print_error("krb5_cc_initialize", context
, ret
);
401 if (!ret
&& (ret
= krb5_cc_store_cred(context
, ccdef
, &credentials
))) {
402 sip_sec_krb5_print_error("krb5_cc_store_cred", context
, ret
);
406 SIPE_DEBUG_INFO_NOFORMAT("sip_sec_krb5_obtain_tgt: new TGT stored in default credentials cache");
411 krb5_free_principal(context
, principal
);
414 krb5_free_context(context
);
418 sip_sec_krb5_print_error(const char *func
,
419 krb5_context context
,
422 const char *error_message
= krb5_get_error_message(context
, ret
);
423 SIPE_DEBUG_ERROR("Kerberos 5 ERROR in %s: %s", func
, error_message
);
424 krb5_free_error_message(context
, error_message
);