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
27 #include <gssapi/gssapi.h>
28 #include <gssapi/gssapi_krb5.h>
31 #include "sip-sec-mech.h"
32 #include "sip-sec-krb5.h"
34 /* Security context for Kerberos */
35 typedef struct _context_krb5
{
36 struct sip_sec_context common
;
37 gss_cred_id_t cred_krb5
;
38 gss_ctx_id_t ctx_krb5
;
41 void sip_sec_krb5_print_gss_error(char *func
, OM_uint32 ret
, OM_uint32 minor
);
44 sip_sec_krb5_obtain_tgt(const char *realm
,
46 const char *password
);
48 /* sip-sec-mech.h API implementation for Kerberos/GSS-API */
51 * Depending on Single Sign-On flag (sso),
52 * obtains existing credentials stored in credentials cash in case of Kerberos,
53 * or attemps to obtain TGT on its own first.
56 sip_sec_acquire_cred__krb5(SipSecContext context
,
64 gss_cred_id_t credentials
;
67 /* Do not use default credentials, obtain a new one and store it in cache */
68 sip_sec_krb5_obtain_tgt(g_ascii_strup(domain
, -1), username
, password
);
71 /* Acquire default user credentials */
72 ret
= gss_acquire_cred(&minor
,
82 sip_sec_krb5_print_gss_error("gss_acquire_cred", ret
, minor
);
83 printf("ERROR: sip_sec_acquire_cred0__krb5: failed to acquire credentials. ret=%d\n", (int)ret
);
84 return SIP_SEC_E_INTERNAL_ERROR
;
86 ((context_krb5
)context
)->cred_krb5
= credentials
;
92 sip_sec_init_sec_context__krb5(SipSecContext context
,
94 SipSecBuffer
*out_buff
,
95 const char *service_name
)
100 OM_uint32 request_flags
;
101 OM_uint32 response_flags
;
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 input_name_buffer
.value
= (void *)service_name
;
109 input_name_buffer
.length
= strlen(input_name_buffer
.value
) + 1;
111 ret
= gss_import_name(&minor
,
113 (const gss_OID
)GSS_KRB5_NT_PRINCIPAL_NAME
,
115 if (GSS_ERROR(ret
)) {
116 sip_sec_krb5_print_gss_error("gss_import_name", ret
, minor
);
117 printf("ERROR: sip_sec_init_sec_context__krb5: failed to construct target name. Returned. ret=%d\n", (int)ret
);
118 return SIP_SEC_E_INTERNAL_ERROR
;
121 request_flags
= GSS_C_INTEG_FLAG
;
123 input_token
.length
= in_buff
.length
;
124 input_token
.value
= in_buff
.value
;
126 output_token
.length
= 0;
127 output_token
.value
= NULL
;
129 ret
= gss_init_sec_context(&minor
,
136 GSS_C_NO_CHANNEL_BINDINGS
,
143 if (GSS_ERROR(ret
)) {
144 sip_sec_krb5_print_gss_error("gss_init_sec_context", ret
, minor
);
145 printf("ERROR: sip_sec_init_sec_context__krb5: failed to initialize context. ret=%d\n", (int)ret
);
146 return SIP_SEC_E_INTERNAL_ERROR
;
148 ret
= gss_release_cred(&minor
, &(ctx
->cred_krb5
));
149 if (GSS_ERROR(ret
)) {
150 sip_sec_krb5_print_gss_error("gss_release_cred", ret
, minor
);
151 printf("ERROR: sip_sec_init_sec_context__krb5: failed to release credentials. ret=%d\n", (int)ret
);
154 input_token
.value
= NULL
;
155 input_token
.length
= 0;
157 out_buff
->length
= output_token
.length
;
158 out_buff
->value
= output_token
.value
;
160 context
->expires
= (int)expiry
;
166 * @param message a NULL terminated string to sign
169 sip_sec_make_signature__krb5(SipSecContext context
,
171 SipSecBuffer
*signature
)
175 gss_buffer_desc input_message
;
176 gss_buffer_desc output_token
;
178 input_message
.value
= (void *)message
;
179 input_message
.length
= strlen(input_message
.value
);
181 ret
= gss_get_mic(&minor
,
182 ((context_krb5
)context
)->ctx_krb5
,
187 if (GSS_ERROR(ret
)) {
188 sip_sec_krb5_print_gss_error("gss_get_mic", ret
, minor
);
189 printf("ERROR: sip_ssp_make_signature: failed to make signature. ret=%d\n", (int)ret
);
190 return SIP_SEC_E_INTERNAL_ERROR
;
192 signature
->value
= output_token
.value
;
193 signature
->length
= output_token
.length
;
200 * @param message a NULL terminated string to check signature of
203 sip_sec_verify_signature__krb5(SipSecContext context
,
205 SipSecBuffer signature
)
210 gss_buffer_desc input_message
;
211 gss_buffer_desc input_token
;
213 input_message
.value
= (void *)message
;
214 input_message
.length
= strlen(input_message
.value
);
216 input_token
.value
= signature
.value
;
217 input_token
.length
= signature
.length
;
219 ret
= gss_verify_mic(&minor
,
220 ((context_krb5
)context
)->ctx_krb5
,
225 if (GSS_ERROR(ret
)) {
226 sip_sec_krb5_print_gss_error("gss_verify_mic", ret
, minor
);
227 printf("ERROR: sip_sec_verify_signature__krb5: failed to make signature. ret=%d\n", (int)ret
);
228 return SIP_SEC_E_INTERNAL_ERROR
;
235 sip_sec_destroy_sec_context__krb5(SipSecContext context
)
239 context_krb5 ctx
= (context_krb5
) context
;
241 if (ctx
->cred_krb5
) {
242 ret
= gss_release_cred(&minor
, &(ctx
->cred_krb5
));
243 if (GSS_ERROR(ret
)) {
244 sip_sec_krb5_print_gss_error("gss_release_cred", ret
, minor
);
245 printf("ERROR: sip_sec_destroy_sec_context__krb5: failed to release credentials. ret=%d\n", (int)ret
);
250 ret
= gss_delete_sec_context(&minor
, &(ctx
->ctx_krb5
), GSS_C_NO_BUFFER
);
251 if (GSS_ERROR(ret
)) {
252 sip_sec_krb5_print_gss_error("gss_delete_sec_context", ret
, minor
);
253 printf("ERROR: sip_sec_destroy_sec_context__krb5: failed to delete security context. ret=%d\n", (int)ret
);
261 sip_sec_create_context__krb5(SIPE_UNUSED_PARAMETER SipSecAuthType type
)
263 context_krb5 context
= g_malloc0(sizeof(struct _context_krb5
));
264 if (!context
) return(NULL
);
266 context
->common
.acquire_cred_func
= sip_sec_acquire_cred__krb5
;
267 context
->common
.init_context_func
= sip_sec_init_sec_context__krb5
;
268 context
->common
.destroy_context_func
= sip_sec_destroy_sec_context__krb5
;
269 context
->common
.make_signature_func
= sip_sec_make_signature__krb5
;
270 context
->common
.verify_signature_func
= sip_sec_verify_signature__krb5
;
272 return((SipSecContext
) context
);
277 sip_sec_krb5_print_gss_error0(char *func
,
283 OM_uint32 message_context
= 0;
284 gss_buffer_desc status_string
;
287 ret
= gss_display_status(&minor
,
294 printf("GSS-API error in %s (%s): %s\n", func
, (type
== GSS_C_GSS_CODE
? "GSS" : "Mech"), (char *)status_string
.value
);
295 gss_release_buffer(&minor
, &status_string
);
296 } while (message_context
!= 0);
300 * Prints out errors of GSS-API function invocation
302 void sip_sec_krb5_print_gss_error(char *func
, OM_uint32 ret
, OM_uint32 minor
)
304 sip_sec_krb5_print_gss_error0(func
, ret
, GSS_C_GSS_CODE
);
305 sip_sec_krb5_print_gss_error0(func
, minor
, GSS_C_MECH_CODE
);
309 * Prints out errors of Kerberos 5 function invocation
312 sip_sec_krb5_print_error(const char *func
,
313 krb5_context context
,
314 krb5_error_code ret
);
317 * Obtains Kerberos TGT and stores it in default credentials cache.
318 * Similar what kinit util would do.
319 * Can be checked with klist util.
321 * kinit would require the following name:
322 * alice@ATLANTA.LOCAL
323 * where 'alice' is a username and
324 * 'ATLANTA.LOCAL' is a realm (domain) .
327 sip_sec_krb5_obtain_tgt(SIPE_UNUSED_PARAMETER
const char *realm_in
,
328 const char *username_in
,
329 const char *password
)
331 krb5_context context
;
332 krb5_principal principal
;
333 krb5_creds credentials
;
341 printf("sip_sec_krb5_obtain_tgt started\n");
343 memset(&credentials
, 0, sizeof(krb5_creds
));
345 /* extracts realm as domain part of username
346 * either before '\' or after '@'
348 domain_user
= g_strsplit(username_in
, "\\", 2);
349 if (domain_user
&& domain_user
[1]) {
350 realm
= g_ascii_strup(domain_user
[0], -1);
351 username
= g_strdup(domain_user
[1]);
353 realm
= g_strdup("");
354 username
= g_strdup(username_in
);
356 g_strfreev(domain_user
);
358 user_realm
= g_strsplit(username
, "@", 2);
359 if (user_realm
&& user_realm
[1]) {
362 username
= g_strdup(user_realm
[0]);
363 realm
= g_ascii_strup(user_realm
[1], -1);
365 g_strfreev(user_realm
);
368 if ((ret
= krb5_init_context(&context
))) {
369 sip_sec_krb5_print_error("krb5_init_context", context
, ret
);
372 if (!ret
&& (ret
= krb5_build_principal(context
, &principal
, strlen(realm
), realm
, username
, NULL
))) {
373 sip_sec_krb5_print_error("krb5_build_principal", context
, ret
);
378 if (!ret
&& (ret
= krb5_get_init_creds_password(context
, &credentials
, principal
, (char *)password
, NULL
, NULL
, 0, NULL
, NULL
))) {
379 sip_sec_krb5_print_error("krb5_get_init_creds_password", context
, ret
);
383 printf("sip_sec_krb5_obtain_tgt: new TGT obtained.\n");
387 /* Store TGT in default credential cache */
388 if (!ret
&& (ret
= krb5_cc_default(context
, &ccdef
))) {
389 sip_sec_krb5_print_error("krb5_cc_default", context
, ret
);
392 if (!ret
&& (ret
= krb5_cc_initialize(context
, ccdef
, credentials
.client
))) {
393 sip_sec_krb5_print_error("krb5_cc_initialize", context
, ret
);
396 if (!ret
&& (ret
= krb5_cc_store_cred(context
, ccdef
, &credentials
))) {
397 sip_sec_krb5_print_error("krb5_cc_store_cred", context
, ret
);
401 printf("sip_sec_krb5_obtain_tgt: new TGT stored in default credentials cache.\n");
406 krb5_free_principal(context
, principal
);
409 krb5_free_context(context
);
413 sip_sec_krb5_print_error(const char *func
,
414 krb5_context context
,
417 #if defined(HAVE_KRB5_GET_ERROR_MESSAGE)
418 const char *error_message
= krb5_get_error_message(context
, ret
);
419 printf("Kerberos 5 ERROR in %s: %s\n", func
, error_message
);
420 krb5_free_error_message(context
, error_message
);
422 printf("Kerberos 5 ERROR in %s: %s\n", func
, "unknown error");