Merge branch 'mob' of git+ssh://mob@repo.or.cz/srv/git/siplcs into mob
[siplcs.git] / src / core / sip-sec-krb5.c
blob4513dd22511b1f9b4be99ec36fd9e2fc57a82462
1 /**
2 * @file sip-sec-krb5.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 <glib.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <gssapi/gssapi.h>
28 #include <gssapi/gssapi_krb5.h>
30 #include "sipe-common.h"
31 #include "sip-sec.h"
32 #include "sip-sec-mech.h"
33 #include "sip-sec-krb5.h"
35 /* Security context for Kerberos */
36 typedef struct _context_krb5 {
37 struct sip_sec_context common;
38 gss_cred_id_t cred_krb5;
39 gss_ctx_id_t ctx_krb5;
40 } *context_krb5;
42 void sip_sec_krb5_print_gss_error(char *func, OM_uint32 ret, OM_uint32 minor);
44 void
45 sip_sec_krb5_obtain_tgt(const char *realm,
46 const char *username,
47 const char *password);
49 /* sip-sec-mech.h API implementation for Kerberos/GSS-API */
51 /**
52 * Depending on Single Sign-On flag (sso),
53 * obtains existing credentials stored in credentials cash in case of Kerberos,
54 * or attemps to obtain TGT on its own first.
56 static sip_uint32
57 sip_sec_acquire_cred__krb5(SipSecContext context,
58 const char *domain,
59 const char *username,
60 const char *password)
62 OM_uint32 ret;
63 OM_uint32 minor;
64 OM_uint32 expiry;
65 gss_cred_id_t credentials;
67 if (!context->sso) {
68 /* Do not use default credentials, obtain a new one and store it in cache */
69 sip_sec_krb5_obtain_tgt(g_ascii_strup(domain, -1), username, password);
72 /* Acquire default user credentials */
73 ret = gss_acquire_cred(&minor,
74 GSS_C_NO_NAME,
75 GSS_C_INDEFINITE,
76 GSS_C_NO_OID_SET,
77 GSS_C_INITIATE,
78 &credentials,
79 NULL,
80 &expiry);
82 if (GSS_ERROR(ret)) {
83 sip_sec_krb5_print_gss_error("gss_acquire_cred", ret, minor);
84 printf("ERROR: sip_sec_acquire_cred0__krb5: failed to acquire credentials. ret=%d\n", (int)ret);
85 return SIP_SEC_E_INTERNAL_ERROR;
86 } else {
87 ((context_krb5)context)->cred_krb5 = credentials;
88 return SIP_SEC_E_OK;
92 static sip_uint32
93 sip_sec_init_sec_context__krb5(SipSecContext context,
94 SipSecBuffer in_buff,
95 SipSecBuffer *out_buff,
96 const char *service_name)
98 OM_uint32 ret;
99 OM_uint32 minor;
100 OM_uint32 expiry;
101 OM_uint32 request_flags;
102 OM_uint32 response_flags;
103 gss_buffer_desc input_token;
104 gss_buffer_desc output_token;
105 gss_buffer_desc input_name_buffer;
106 gss_name_t target_name;
107 context_krb5 ctx = (context_krb5) context;
109 input_name_buffer.value = (void *)service_name;
110 input_name_buffer.length = strlen(input_name_buffer.value) + 1;
112 ret = gss_import_name(&minor,
113 &input_name_buffer,
114 (gss_OID) GSS_KRB5_NT_PRINCIPAL_NAME,
115 &target_name);
116 if (GSS_ERROR(ret)) {
117 sip_sec_krb5_print_gss_error("gss_import_name", ret, minor);
118 printf("ERROR: sip_sec_init_sec_context__krb5: failed to construct target name. Returned. ret=%d\n", (int)ret);
119 return SIP_SEC_E_INTERNAL_ERROR;
122 request_flags = GSS_C_INTEG_FLAG;
124 input_token.length = in_buff.length;
125 input_token.value = in_buff.value;
127 output_token.length = 0;
128 output_token.value = NULL;
130 ret = gss_init_sec_context(&minor,
131 ctx->cred_krb5,
132 &(ctx->ctx_krb5),
133 target_name,
134 GSS_C_NO_OID,
135 request_flags,
136 GSS_C_INDEFINITE,
137 GSS_C_NO_CHANNEL_BINDINGS,
138 &input_token,
139 NULL,
140 &output_token,
141 &response_flags,
142 &expiry);
144 if (GSS_ERROR(ret)) {
145 sip_sec_krb5_print_gss_error("gss_init_sec_context", ret, minor);
146 printf("ERROR: sip_sec_init_sec_context__krb5: failed to initialize context. ret=%d\n", (int)ret);
147 return SIP_SEC_E_INTERNAL_ERROR;
148 } else {
149 ret = gss_release_cred(&minor, &(ctx->cred_krb5));
150 if (GSS_ERROR(ret)) {
151 sip_sec_krb5_print_gss_error("gss_release_cred", ret, minor);
152 printf("ERROR: sip_sec_init_sec_context__krb5: failed to release credentials. ret=%d\n", (int)ret);
155 input_token.value = NULL;
156 input_token.length = 0;
158 out_buff->length = output_token.length;
159 out_buff->value = output_token.value;
161 context->expires = (int)expiry;
162 return SIP_SEC_E_OK;
167 * @param message a NULL terminated string to sign
169 static sip_uint32
170 sip_sec_make_signature__krb5(SipSecContext context,
171 const char *message,
172 SipSecBuffer *signature)
174 OM_uint32 ret;
175 OM_uint32 minor;
176 gss_buffer_desc input_message;
177 gss_buffer_desc output_token;
179 input_message.value = (void *)message;
180 input_message.length = strlen(input_message.value);
182 ret = gss_get_mic(&minor,
183 ((context_krb5)context)->ctx_krb5,
184 GSS_C_QOP_DEFAULT,
185 &input_message,
186 &output_token);
188 if (GSS_ERROR(ret)) {
189 sip_sec_krb5_print_gss_error("gss_get_mic", ret, minor);
190 printf("ERROR: sip_ssp_make_signature: failed to make signature. ret=%d\n", (int)ret);
191 return SIP_SEC_E_INTERNAL_ERROR;
192 } else {
193 signature->value = output_token.value;
194 signature->length = output_token.length;
196 return SIP_SEC_E_OK;
201 * @param message a NULL terminated string to check signature of
203 static sip_uint32
204 sip_sec_verify_signature__krb5(SipSecContext context,
205 const char *message,
206 SipSecBuffer signature)
208 OM_uint32 ret;
209 OM_uint32 minor;
210 gss_qop_t qop_state;
211 gss_buffer_desc input_message;
212 gss_buffer_desc input_token;
214 input_message.value = (void *)message;
215 input_message.length = strlen(input_message.value);
217 input_token.value = signature.value;
218 input_token.length = signature.length;
220 ret = gss_verify_mic(&minor,
221 ((context_krb5)context)->ctx_krb5,
222 &input_message,
223 &input_token,
224 &qop_state);
226 if (GSS_ERROR(ret)) {
227 sip_sec_krb5_print_gss_error("gss_verify_mic", ret, minor);
228 printf("ERROR: sip_sec_verify_signature__krb5: failed to make signature. ret=%d\n", (int)ret);
229 return SIP_SEC_E_INTERNAL_ERROR;
230 } else {
231 return SIP_SEC_E_OK;
235 static void
236 sip_sec_destroy_sec_context__krb5(SipSecContext context)
238 OM_uint32 ret;
239 OM_uint32 minor;
240 context_krb5 ctx = (context_krb5) context;
242 if (ctx->cred_krb5) {
243 ret = gss_release_cred(&minor, &(ctx->cred_krb5));
244 if (GSS_ERROR(ret)) {
245 sip_sec_krb5_print_gss_error("gss_release_cred", ret, minor);
246 printf("ERROR: sip_sec_destroy_sec_context__krb5: failed to release credentials. ret=%d\n", (int)ret);
250 if (ctx->ctx_krb5) {
251 ret = gss_delete_sec_context(&minor, &(ctx->ctx_krb5), GSS_C_NO_BUFFER);
252 if (GSS_ERROR(ret)) {
253 sip_sec_krb5_print_gss_error("gss_delete_sec_context", ret, minor);
254 printf("ERROR: sip_sec_destroy_sec_context__krb5: failed to delete security context. ret=%d\n", (int)ret);
258 g_free(ctx);
261 SipSecContext
262 sip_sec_create_context__krb5(SIPE_UNUSED_PARAMETER SipSecAuthType type)
264 context_krb5 context = g_malloc0(sizeof(struct _context_krb5));
265 if (!context) return(NULL);
267 context->common.acquire_cred_func = sip_sec_acquire_cred__krb5;
268 context->common.init_context_func = sip_sec_init_sec_context__krb5;
269 context->common.destroy_context_func = sip_sec_destroy_sec_context__krb5;
270 context->common.make_signature_func = sip_sec_make_signature__krb5;
271 context->common.verify_signature_func = sip_sec_verify_signature__krb5;
273 return((SipSecContext) context);
277 static void
278 sip_sec_krb5_print_gss_error0(char *func,
279 OM_uint32 status,
280 int type)
282 OM_uint32 ret;
283 OM_uint32 minor;
284 OM_uint32 message_context = 0;
285 gss_buffer_desc status_string;
287 do {
288 ret = gss_display_status(&minor,
289 status,
290 type,
291 GSS_C_NO_OID,
292 &message_context,
293 &status_string);
295 printf("GSS-API error in %s (%s): %s\n", func, (type == GSS_C_GSS_CODE ? "GSS" : "Mech"), (char *)status_string.value);
296 gss_release_buffer(&minor, &status_string);
297 } while (message_context != 0);
301 * Prints out errors of GSS-API function invocation
303 void sip_sec_krb5_print_gss_error(char *func, OM_uint32 ret, OM_uint32 minor)
305 sip_sec_krb5_print_gss_error0(func, ret, GSS_C_GSS_CODE);
306 sip_sec_krb5_print_gss_error0(func, minor, GSS_C_MECH_CODE);
310 * Prints out errors of Kerberos 5 function invocation
312 void
313 sip_sec_krb5_print_error(const char *func,
314 krb5_context context,
315 krb5_error_code ret);
318 * Obtains Kerberos TGT and stores it in default credentials cache.
319 * Similar what kinit util would do.
320 * Can be checked with klist util.
322 * kinit would require the following name:
323 * alice@ATLANTA.LOCAL
324 * where 'alice' is a username and
325 * 'ATLANTA.LOCAL' is a realm (domain) .
327 void
328 sip_sec_krb5_obtain_tgt(SIPE_UNUSED_PARAMETER const char *realm_in,
329 const char *username_in,
330 const char *password)
332 krb5_context context;
333 krb5_principal principal = NULL;
334 krb5_creds credentials;
335 krb5_ccache ccdef;
336 krb5_error_code ret;
337 char *realm;
338 char *username;
339 gchar **domain_user;
340 gchar **user_realm;
342 printf("sip_sec_krb5_obtain_tgt started\n");
344 memset(&credentials, 0, sizeof(krb5_creds));
346 /* extracts realm as domain part of username
347 * either before '\' or after '@'
349 domain_user = g_strsplit(username_in, "\\", 2);
350 if (domain_user && domain_user[1]) {
351 realm = g_ascii_strup(domain_user[0], -1);
352 username = g_strdup(domain_user[1]);
353 } else {
354 realm = g_strdup("");
355 username = g_strdup(username_in);
357 g_strfreev(domain_user);
359 user_realm = g_strsplit(username, "@", 2);
360 if (user_realm && user_realm[1]) {
361 g_free(username);
362 g_free(realm);
363 username = g_strdup(user_realm[0]);
364 realm = g_ascii_strup(user_realm[1], -1);
366 g_strfreev(user_realm);
368 /* Obtait TGT */
369 if ((ret = krb5_init_context(&context))) {
370 sip_sec_krb5_print_error("krb5_init_context", context, ret);
373 if (!ret && (ret = krb5_build_principal(context, &principal, strlen(realm), realm, username, NULL))) {
374 sip_sec_krb5_print_error("krb5_build_principal", context, ret);
376 g_free(username);
377 g_free(realm);
379 if (!ret && (ret = krb5_get_init_creds_password(context, &credentials, principal, (char *)password, NULL, NULL, 0, NULL, NULL))) {
380 sip_sec_krb5_print_error("krb5_get_init_creds_password", context, ret);
383 if (!ret) {
384 printf("sip_sec_krb5_obtain_tgt: new TGT obtained.\n");
388 /* Store TGT in default credential cache */
389 if (!ret && (ret = krb5_cc_default(context, &ccdef))) {
390 sip_sec_krb5_print_error("krb5_cc_default", context, ret);
393 if (!ret && (ret = krb5_cc_initialize(context, ccdef, credentials.client))) {
394 sip_sec_krb5_print_error("krb5_cc_initialize", context, ret);
397 if (!ret && (ret = krb5_cc_store_cred(context, ccdef, &credentials))) {
398 sip_sec_krb5_print_error("krb5_cc_store_cred", context, ret);
401 if (!ret) {
402 printf("sip_sec_krb5_obtain_tgt: new TGT stored in default credentials cache.\n");
406 if (principal)
407 krb5_free_principal(context, principal);
409 if (context)
410 krb5_free_context(context);
413 #if defined(HAVE_KRB5_GET_ERROR_MESSAGE)
414 void
415 sip_sec_krb5_print_error(const char *func,
416 krb5_context context,
417 krb5_error_code ret)
419 const char *error_message = krb5_get_error_message(context, ret);
420 printf("Kerberos 5 ERROR in %s: %s\n", func, error_message);
421 krb5_free_error_message(context, error_message);
423 #else
424 void
425 sip_sec_krb5_print_error(const char *func,
426 SIPE_UNUSED_PARAMETER krb5_context context,
427 SIPE_UNUSED_PARAMETER krb5_error_code ret)
429 printf("Kerberos 5 ERROR in %s: %s\n", func, "unknown error");
431 #endif
434 Local Variables:
435 mode: c
436 c-file-style: "bsd"
437 indent-tabs-mode: t
438 tab-width: 8
439 End: