Feature #3578132: Kerberos configuration should be passwordless
[siplcs.git] / src / core / sip-sec-krb5.c
blob785436240c8ede5333908885d451fb98ca1270f3
1 /**
2 * @file sip-sec-krb5.c
4 * pidgin-sipe
6 * Copyright (C) 2010,2012 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 #include <glib.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <gssapi/gssapi.h>
29 #include <gssapi/gssapi_krb5.h>
30 #include <krb5.h>
32 #include "sipe-common.h"
33 #include "sip-sec.h"
34 #include "sip-sec-mech.h"
35 #include "sip-sec-krb5.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;
42 } *context_krb5;
44 void sip_sec_krb5_print_gss_error(char *func, OM_uint32 ret, OM_uint32 minor);
46 void
47 sip_sec_krb5_obtain_tgt(const char *realm,
48 const char *username,
49 const char *password);
51 /* sip-sec-mech.h API implementation for Kerberos/GSS-API */
53 /**
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.
58 static sip_uint32
59 sip_sec_acquire_cred__krb5(SipSecContext context,
60 const char *domain,
61 const char *username,
62 const char *password)
64 OM_uint32 ret;
65 OM_uint32 minor;
66 OM_uint32 expiry;
67 gss_cred_id_t credentials;
69 if (!context->sso) {
70 /* Do not use default credentials, obtain a new one and store it in cache */
71 sip_sec_krb5_obtain_tgt(g_ascii_strup(domain, -1), username, password);
74 /* Acquire default user credentials */
75 ret = gss_acquire_cred(&minor,
76 GSS_C_NO_NAME,
77 GSS_C_INDEFINITE,
78 GSS_C_NO_OID_SET,
79 GSS_C_INITIATE,
80 &credentials,
81 NULL,
82 &expiry);
84 if (GSS_ERROR(ret)) {
85 sip_sec_krb5_print_gss_error("gss_acquire_cred", ret, minor);
86 printf("ERROR: sip_sec_acquire_cred0__krb5: failed to acquire credentials. ret=%d\n", (int)ret);
87 return SIP_SEC_E_INTERNAL_ERROR;
88 } else {
89 ((context_krb5)context)->cred_krb5 = credentials;
90 return SIP_SEC_E_OK;
94 static sip_uint32
95 sip_sec_init_sec_context__krb5(SipSecContext context,
96 SipSecBuffer in_buff,
97 SipSecBuffer *out_buff,
98 const char *service_name)
100 OM_uint32 ret;
101 OM_uint32 minor;
102 OM_uint32 expiry;
103 OM_uint32 request_flags;
104 OM_uint32 response_flags;
105 gss_buffer_desc input_token;
106 gss_buffer_desc output_token;
107 gss_buffer_desc input_name_buffer;
108 gss_name_t target_name;
109 context_krb5 ctx = (context_krb5) context;
111 input_name_buffer.value = (void *)service_name;
112 input_name_buffer.length = strlen(input_name_buffer.value) + 1;
114 ret = gss_import_name(&minor,
115 &input_name_buffer,
116 (gss_OID) GSS_KRB5_NT_PRINCIPAL_NAME,
117 &target_name);
118 if (GSS_ERROR(ret)) {
119 sip_sec_krb5_print_gss_error("gss_import_name", ret, minor);
120 printf("ERROR: sip_sec_init_sec_context__krb5: failed to construct target name. Returned. ret=%d\n", (int)ret);
121 return SIP_SEC_E_INTERNAL_ERROR;
124 request_flags = GSS_C_INTEG_FLAG;
126 input_token.length = in_buff.length;
127 input_token.value = in_buff.value;
129 output_token.length = 0;
130 output_token.value = NULL;
132 ret = gss_init_sec_context(&minor,
133 ctx->cred_krb5,
134 &(ctx->ctx_krb5),
135 target_name,
136 GSS_C_NO_OID,
137 request_flags,
138 GSS_C_INDEFINITE,
139 GSS_C_NO_CHANNEL_BINDINGS,
140 &input_token,
141 NULL,
142 &output_token,
143 &response_flags,
144 &expiry);
146 if (GSS_ERROR(ret)) {
147 sip_sec_krb5_print_gss_error("gss_init_sec_context", ret, minor);
148 printf("ERROR: sip_sec_init_sec_context__krb5: failed to initialize context. ret=%d\n", (int)ret);
149 return SIP_SEC_E_INTERNAL_ERROR;
150 } else {
151 ret = gss_release_cred(&minor, &(ctx->cred_krb5));
152 if (GSS_ERROR(ret)) {
153 sip_sec_krb5_print_gss_error("gss_release_cred", ret, minor);
154 printf("ERROR: sip_sec_init_sec_context__krb5: failed to release credentials. ret=%d\n", (int)ret);
157 input_token.value = NULL;
158 input_token.length = 0;
160 out_buff->length = output_token.length;
161 out_buff->value = output_token.value;
163 context->expires = (int)expiry;
164 return SIP_SEC_E_OK;
169 * @param message a NULL terminated string to sign
171 static sip_uint32
172 sip_sec_make_signature__krb5(SipSecContext context,
173 const char *message,
174 SipSecBuffer *signature)
176 OM_uint32 ret;
177 OM_uint32 minor;
178 gss_buffer_desc input_message;
179 gss_buffer_desc output_token;
181 input_message.value = (void *)message;
182 input_message.length = strlen(input_message.value);
184 ret = gss_get_mic(&minor,
185 ((context_krb5)context)->ctx_krb5,
186 GSS_C_QOP_DEFAULT,
187 &input_message,
188 &output_token);
190 if (GSS_ERROR(ret)) {
191 sip_sec_krb5_print_gss_error("gss_get_mic", ret, minor);
192 printf("ERROR: sip_ssp_make_signature: failed to make signature. ret=%d\n", (int)ret);
193 return SIP_SEC_E_INTERNAL_ERROR;
194 } else {
195 signature->value = output_token.value;
196 signature->length = output_token.length;
198 return SIP_SEC_E_OK;
203 * @param message a NULL terminated string to check signature of
205 static sip_uint32
206 sip_sec_verify_signature__krb5(SipSecContext context,
207 const char *message,
208 SipSecBuffer signature)
210 OM_uint32 ret;
211 OM_uint32 minor;
212 gss_qop_t qop_state;
213 gss_buffer_desc input_message;
214 gss_buffer_desc input_token;
216 input_message.value = (void *)message;
217 input_message.length = strlen(input_message.value);
219 input_token.value = signature.value;
220 input_token.length = signature.length;
222 ret = gss_verify_mic(&minor,
223 ((context_krb5)context)->ctx_krb5,
224 &input_message,
225 &input_token,
226 &qop_state);
228 if (GSS_ERROR(ret)) {
229 sip_sec_krb5_print_gss_error("gss_verify_mic", ret, minor);
230 printf("ERROR: sip_sec_verify_signature__krb5: failed to make signature. ret=%d\n", (int)ret);
231 return SIP_SEC_E_INTERNAL_ERROR;
232 } else {
233 return SIP_SEC_E_OK;
237 static void
238 sip_sec_destroy_sec_context__krb5(SipSecContext context)
240 OM_uint32 ret;
241 OM_uint32 minor;
242 context_krb5 ctx = (context_krb5) context;
244 if (ctx->cred_krb5) {
245 ret = gss_release_cred(&minor, &(ctx->cred_krb5));
246 if (GSS_ERROR(ret)) {
247 sip_sec_krb5_print_gss_error("gss_release_cred", ret, minor);
248 printf("ERROR: sip_sec_destroy_sec_context__krb5: failed to release credentials. ret=%d\n", (int)ret);
252 if (ctx->ctx_krb5) {
253 ret = gss_delete_sec_context(&minor, &(ctx->ctx_krb5), GSS_C_NO_BUFFER);
254 if (GSS_ERROR(ret)) {
255 sip_sec_krb5_print_gss_error("gss_delete_sec_context", ret, minor);
256 printf("ERROR: sip_sec_destroy_sec_context__krb5: failed to delete security context. ret=%d\n", (int)ret);
260 g_free(ctx);
263 SipSecContext
264 sip_sec_create_context__krb5(SIPE_UNUSED_PARAMETER guint type)
266 context_krb5 context = g_malloc0(sizeof(struct _context_krb5));
267 if (!context) return(NULL);
269 context->common.acquire_cred_func = sip_sec_acquire_cred__krb5;
270 context->common.init_context_func = sip_sec_init_sec_context__krb5;
271 context->common.destroy_context_func = sip_sec_destroy_sec_context__krb5;
272 context->common.make_signature_func = sip_sec_make_signature__krb5;
273 context->common.verify_signature_func = sip_sec_verify_signature__krb5;
275 return((SipSecContext) context);
278 gboolean sip_sec_password__krb5(void)
280 /* Kerberos supports Single-Sign On */
281 return(FALSE);
284 static void
285 sip_sec_krb5_print_gss_error0(char *func,
286 OM_uint32 status,
287 int type)
289 OM_uint32 minor;
290 OM_uint32 message_context = 0;
291 gss_buffer_desc status_string;
293 do {
294 gss_display_status(&minor,
295 status,
296 type,
297 GSS_C_NO_OID,
298 &message_context,
299 &status_string);
301 printf("GSS-API error in %s (%s): %s\n", func, (type == GSS_C_GSS_CODE ? "GSS" : "Mech"), (char *)status_string.value);
302 gss_release_buffer(&minor, &status_string);
303 } while (message_context != 0);
307 * Prints out errors of GSS-API function invocation
309 void sip_sec_krb5_print_gss_error(char *func, OM_uint32 ret, OM_uint32 minor)
311 sip_sec_krb5_print_gss_error0(func, ret, GSS_C_GSS_CODE);
312 sip_sec_krb5_print_gss_error0(func, minor, GSS_C_MECH_CODE);
316 * Prints out errors of Kerberos 5 function invocation
318 void
319 sip_sec_krb5_print_error(const char *func,
320 krb5_context context,
321 krb5_error_code ret);
324 * Obtains Kerberos TGT and stores it in default credentials cache.
325 * Similar what kinit util would do.
326 * Can be checked with klist util.
328 * kinit would require the following name:
329 * alice@ATLANTA.LOCAL
330 * where 'alice' is a username and
331 * 'ATLANTA.LOCAL' is a realm (domain) .
333 void
334 sip_sec_krb5_obtain_tgt(SIPE_UNUSED_PARAMETER const char *realm_in,
335 const char *username_in,
336 const char *password)
338 krb5_context context;
339 krb5_principal principal = NULL;
340 krb5_creds credentials;
341 krb5_ccache ccdef;
342 krb5_error_code ret;
343 char *realm;
344 char *username;
345 gchar **domain_user;
346 gchar **user_realm;
348 printf("sip_sec_krb5_obtain_tgt started\n");
350 memset(&credentials, 0, sizeof(krb5_creds));
352 /* extracts realm as domain part of username
353 * either before '\' or after '@'
355 domain_user = g_strsplit(username_in, "\\", 2);
356 if (domain_user && domain_user[1]) {
357 realm = g_ascii_strup(domain_user[0], -1);
358 username = g_strdup(domain_user[1]);
359 } else {
360 realm = g_strdup("");
361 username = g_strdup(username_in);
363 g_strfreev(domain_user);
365 user_realm = g_strsplit(username, "@", 2);
366 if (user_realm && user_realm[1]) {
367 g_free(username);
368 g_free(realm);
369 username = g_strdup(user_realm[0]);
370 realm = g_ascii_strup(user_realm[1], -1);
372 g_strfreev(user_realm);
374 /* Obtait TGT */
375 if ((ret = krb5_init_context(&context))) {
376 sip_sec_krb5_print_error("krb5_init_context", context, ret);
379 if (!ret && (ret = krb5_build_principal(context, &principal, strlen(realm), realm, username, NULL))) {
380 sip_sec_krb5_print_error("krb5_build_principal", context, ret);
382 g_free(username);
383 g_free(realm);
385 if (!ret && (ret = krb5_get_init_creds_password(context, &credentials, principal, (char *)password, NULL, NULL, 0, NULL, NULL))) {
386 sip_sec_krb5_print_error("krb5_get_init_creds_password", context, ret);
389 if (!ret) {
390 printf("sip_sec_krb5_obtain_tgt: new TGT obtained.\n");
394 /* Store TGT in default credential cache */
395 if (!ret && (ret = krb5_cc_default(context, &ccdef))) {
396 sip_sec_krb5_print_error("krb5_cc_default", context, ret);
399 if (!ret && (ret = krb5_cc_initialize(context, ccdef, credentials.client))) {
400 sip_sec_krb5_print_error("krb5_cc_initialize", context, ret);
403 if (!ret && (ret = krb5_cc_store_cred(context, ccdef, &credentials))) {
404 sip_sec_krb5_print_error("krb5_cc_store_cred", context, ret);
407 if (!ret) {
408 printf("sip_sec_krb5_obtain_tgt: new TGT stored in default credentials cache.\n");
412 if (principal)
413 krb5_free_principal(context, principal);
415 if (context)
416 krb5_free_context(context);
419 #if defined(HAVE_KRB5_GET_ERROR_MESSAGE)
420 void
421 sip_sec_krb5_print_error(const char *func,
422 krb5_context context,
423 krb5_error_code ret)
425 const char *error_message = krb5_get_error_message(context, ret);
426 printf("Kerberos 5 ERROR in %s: %s\n", func, error_message);
427 krb5_free_error_message(context, error_message);
429 #else
430 void
431 sip_sec_krb5_print_error(const char *func,
432 SIPE_UNUSED_PARAMETER krb5_context context,
433 SIPE_UNUSED_PARAMETER krb5_error_code ret)
435 printf("Kerberos 5 ERROR in %s: %s\n", func, "unknown error");
437 #endif
440 Local Variables:
441 mode: c
442 c-file-style: "bsd"
443 indent-tabs-mode: t
444 tab-width: 8
445 End: