15 #if HAVE_AUTH_MODULE_NEGOTIATE && HAVE_KRB5 && HAVE_GSSAPI
17 #define KERBEROS_APPLE_DEPRECATED(x)
18 #define GSSKRB_APPLE_DEPRECATED(x)
33 #if HAVE_BROKEN_SOLARIS_KRB5_H
34 #if defined(__cplusplus)
35 #define KRB5INT_BEGIN_DECLS extern "C" {
36 #define KRB5INT_END_DECLS
41 #elif HAVE_ET_COM_ERR_H
42 #include <et/com_err.h>
48 #if HAVE_GSSAPI_GSSAPI_H
49 #include <gssapi/gssapi.h>
54 #if HAVE_GSSAPI_GSSAPI_EXT_H
55 #include <gssapi/gssapi_ext.h>
57 #if HAVE_GSSAPI_GSSAPI_KRB5_H
58 #include <gssapi/gssapi_krb5.h>
60 #if HAVE_GSSAPI_GSSAPI_GENERIC_H
61 #include <gssapi/gssapi_generic.h>
65 #ifndef gss_nt_service_name
66 #define gss_nt_service_name GSS_C_NT_HOSTBASED_SERVICE
69 #if !HAVE_ERROR_MESSAGE && HAVE_KRB5_GET_ERROR_MESSAGE
70 #define error_message(code) krb5_get_error_message(kparam.context,code)
71 #elif !HAVE_ERROR_MESSAGE && HAVE_KRB5_GET_ERR_TEXT
72 #define error_message(code) krb5_get_err_text(kparam.context,code)
73 #elif !HAVE_ERROR_MESSAGE
74 static char err_code[17];
75 const char *KRB5_CALLCONV
76 error_message(
long code) {
77 snprintf(err_code,16,
"%ld",
code);
82 #ifndef gss_mech_spnego
83 static gss_OID_desc _gss_mech_spnego =
84 { 6, (
void *)
"\x2b\x06\x01\x05\x05\x02" };
85 gss_OID gss_mech_spnego = &_gss_mech_spnego;
89 #include <ibm_svc/krb5_svc.h>
90 const char *KRB5_CALLCONV error_message(
long code) {
92 krb5_svc_get_msg(
code, &msg);
102 static struct kstruct {
103 krb5_context context;
117 int krb5_create_cache(
char *keytab_filename,
char *principal_name);
122 void krb5_cleanup(
void);
128 int check_gss_err(OM_uint32 major_status, OM_uint32 minor_status,
129 const char *
function);
131 int check_gss_err(OM_uint32 major_status, OM_uint32 minor_status,
132 const char *
function) {
133 if (GSS_ERROR(major_status)) {
134 OM_uint32 maj_stat, min_stat;
135 OM_uint32 msg_ctx = 0;
136 gss_buffer_desc status_string;
144 maj_stat = gss_display_status(&min_stat, major_status,
145 GSS_C_GSS_CODE, GSS_C_NULL_OID, &msg_ctx, &status_string);
146 if (maj_stat == GSS_S_COMPLETE) {
147 if (
sizeof(buf) > len + status_string.length + 1) {
148 memcpy(buf + len, status_string.value,
149 status_string.length);
150 len += status_string.length;
152 gss_release_buffer(&min_stat, &status_string);
155 gss_release_buffer(&min_stat, &status_string);
157 if (
sizeof(buf) > len + 2) {
158 strcpy(buf + len,
". ");
164 maj_stat = gss_display_status(&min_stat, minor_status,
165 GSS_C_MECH_CODE, GSS_C_NULL_OID, &msg_ctx, &status_string);
166 if (maj_stat == GSS_S_COMPLETE) {
167 if (
sizeof(buf) > len + status_string.length) {
168 memcpy(buf + len, status_string.value,
169 status_string.length);
170 len += status_string.length;
172 gss_release_buffer(&min_stat, &status_string);
175 gss_release_buffer(&min_stat, &status_string);
177 debugs(11, 5,
function <<
"failed: " << buf);
183 void krb5_cleanup() {
184 debugs(11, 5,
"Cleanup kerberos context");
185 if (kparam.context) {
187 krb5_cc_destroy(kparam.context, kparam.cc);
189 krb5_free_context(kparam.context);
190 kparam.context =
NULL;
194 int krb5_create_cache(
char *kf,
char *pn) {
196 #define KT_PATH_MAX 256
197 #define MAX_RENEW_TIME "365d"
198 #define DEFAULT_SKEW (krb5_deltat) 600
200 static char *keytab_filename =
NULL, *principal_name =
NULL;
201 static krb5_keytab keytab = 0;
202 static krb5_keytab_entry entry;
203 static krb5_kt_cursor cursor;
204 static krb5_creds *creds =
NULL;
205 #if USE_HEIMDAL_KRB5 && !HAVE_KRB5_GET_RENEWED_CREDS
206 static krb5_creds creds2;
208 static krb5_principal principal =
NULL;
209 static krb5_deltat skew;
211 #if HAVE_KRB5_GET_INIT_CREDS_OPT_ALLOC
212 krb5_get_init_creds_opt *options;
214 krb5_get_init_creds_opt options;
216 krb5_error_code
code = 0;
218 #if HAVE_PROFILE_H && HAVE_KRB5_GET_PROFILE && HAVE_PROFILE_GET_INTEGER && HAVE_PROFILE_RELEASE
221 #if USE_HEIMDAL_KRB5 && !HAVE_KRB5_GET_RENEWED_CREDS
222 krb5_kdc_flags flags;
223 #if HAVE_KRB5_PRINCIPAL_GET_REALM
224 const char *client_realm;
226 krb5_realm client_realm;
236 (creds->times.endtime - time(0) > skew) &&
237 (creds->times.renew_till - time(0) > 2 * skew)) {
238 if (creds->times.endtime - time(0) < 2 * skew) {
239 #if HAVE_KRB5_GET_RENEWED_CREDS
242 krb5_get_renewed_creds(kparam.context, creds, principal,
247 flags.b.renewable = flags.b.renew = 1;
250 krb5_cc_get_principal(kparam.context, kparam.cc,
255 "Error while getting principal from credential cache : "
256 << error_message(
code));
259 #if HAVE_KRB5_PRINCIPAL_GET_REALM
260 client_realm = krb5_principal_get_realm(kparam.context, principal);
262 client_realm = krb5_princ_realm(kparam.context, creds2.client);
265 krb5_make_principal(kparam.context, &creds2.server,
266 (krb5_const_realm)&client_realm, KRB5_TGS_NAME,
267 (krb5_const_realm)&client_realm,
NULL);
270 "Error while getting krbtgt principal : " <<
271 error_message(
code));
275 krb5_get_kdc_cred(kparam.context, kparam.cc, flags,
NULL,
276 NULL, &creds2, &creds);
277 krb5_free_creds(kparam.context, &creds2);
280 if (
code == KRB5KRB_AP_ERR_TKT_EXPIRED) {
281 krb5_free_creds(kparam.context, creds);
287 "Error while get credentials : " <<
288 error_message(
code));
294 if (!kparam.context) {
295 code = krb5_init_context(&kparam.context);
298 "Error while initialising Kerberos library : "
299 << error_message(
code));
303 #if HAVE_PROFILE_H && HAVE_KRB5_GET_PROFILE && HAVE_PROFILE_GET_INTEGER && HAVE_PROFILE_RELEASE
304 code = krb5_get_profile(kparam.context, &profile);
307 profile_release(profile);
309 "Error while getting profile : " <<
310 error_message(
code));
314 profile_get_integer(profile,
"libdefaults",
"clockskew", 0,
317 profile_release(profile);
320 "Error while getting clockskew : " <<
321 error_message(
code));
324 #elif USE_HEIMDAL_KRB5 && HAVE_KRB5_GET_MAX_TIME_SKEW
325 skew = krb5_get_max_time_skew(kparam.context);
326 #elif USE_HEIMDAL_KRB5 && HAVE_MAX_SKEW_IN_KRB5_CONTEXT
327 skew = kparam.context->max_skew;
333 char buf[KT_PATH_MAX], *p;
335 krb5_kt_default_name(kparam.context, buf, KT_PATH_MAX);
336 p = strchr(buf,
':');
339 xfree(keytab_filename);
340 keytab_filename =
xstrdup(p ? p : buf);
345 code = krb5_kt_resolve(kparam.context, keytab_filename, &keytab);
348 "Error while resolving keytab filename " <<
349 keytab_filename <<
" : " << error_message(
code));
354 code = krb5_kt_start_seq_get(kparam.context, keytab, &cursor);
357 "Error while starting keytab scan : " <<
358 error_message(
code));
362 krb5_kt_next_entry(kparam.context, keytab, &entry, &cursor);
363 krb5_copy_principal(kparam.context, entry.principal,
367 "Error while scanning keytab : " <<
368 error_message(
code));
372 code = krb5_kt_end_seq_get(kparam.context, keytab, &cursor);
375 "Error while ending keytab scan : " <<
376 error_message(
code));
379 #if USE_HEIMDAL_KRB5 || ( HAVE_KRB5_KT_FREE_ENTRY && HAVE_DECL_KRB5_KT_FREE_ENTRY)
380 code = krb5_kt_free_entry(kparam.context, &entry);
382 code = krb5_free_keytab_entry_contents(kparam.context, &entry);
386 "Error while freeing keytab entry : " <<
387 error_message(
code));
397 krb5_parse_name(kparam.context, principal_name, &principal);
400 "Error while parsing principal name " <<
401 principal_name <<
" : " << error_message(
code));
406 creds = (krb5_creds *)
xmalloc(
sizeof(*creds));
407 memset(creds, 0,
sizeof(*creds));
408 #if HAVE_KRB5_GET_INIT_CREDS_OPT_ALLOC
409 krb5_get_init_creds_opt_alloc(kparam.context, &options);
411 krb5_get_init_creds_opt_init(&options);
413 code = krb5_string_to_deltat((
char *) MAX_RENEW_TIME, &rlife);
414 if (
code != 0 || rlife == 0) {
416 "Error bad lifetime value " << MAX_RENEW_TIME <<
417 " : " << error_message(
code));
420 #if HAVE_KRB5_GET_INIT_CREDS_OPT_ALLOC
421 krb5_get_init_creds_opt_set_renew_life(options, rlife);
423 krb5_get_init_creds_keytab(kparam.context, creds, principal,
424 keytab, 0,
NULL, options);
425 #if HAVE_KRB5_GET_INIT_CREDS_FREE_CONTEXT
426 krb5_get_init_creds_opt_free(kparam.context, options);
428 krb5_get_init_creds_opt_free(options);
431 krb5_get_init_creds_opt_set_renew_life(&options, rlife);
433 krb5_get_init_creds_keytab(kparam.context, creds, principal,
434 keytab, 0,
NULL, &options);
439 "Error while initializing credentials from keytab : " <<
440 error_message(
code));
443 #if !HAVE_KRB5_MEMORY_CACHE
445 (
char *)
xmalloc(strlen(
"FILE:/tmp/peer_proxy_negotiate_auth_")
448 debugs(11, 5,
"Error while allocating memory");
452 strlen(
"FILE:/tmp/peer_proxy_negotiate_auth_") + 16,
453 "FILE:/tmp/peer_proxy_negotiate_auth_%d", (
int) getpid());
456 (
char *)
xmalloc(strlen(
"MEMORY:peer_proxy_negotiate_auth_") +
459 debugs(11, 5,
"Error while allocating memory");
463 strlen(
"MEMORY:peer_proxy_negotiate_auth_") + 16,
464 "MEMORY:peer_proxy_negotiate_auth_%d", (
int) getpid());
467 setenv(
"KRB5CCNAME", mem_cache, 1);
468 code = krb5_cc_resolve(kparam.context, mem_cache, &kparam.cc);
472 "Error while resolving memory credential cache : "
473 << error_message(
code));
476 code = krb5_cc_initialize(kparam.context, kparam.cc, principal);
480 "Error while initializing memory credential cache : " <<
481 error_message(
code));
484 code = krb5_cc_store_cred(kparam.context, kparam.cc, creds);
487 "Error while storing credentials : " <<
488 error_message(
code));
492 if (!creds->times.starttime)
493 creds->times.starttime = creds->times.authtime;
502 char *peer_proxy_negotiate_auth(
char *principal_name,
char *proxy,
int flags) {
504 OM_uint32 major_status, minor_status;
505 gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT;
506 gss_name_t server_name = GSS_C_NO_NAME;
507 gss_buffer_desc service = GSS_C_EMPTY_BUFFER;
508 gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
509 gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
512 setbuf(stdout,
NULL);
516 debugs(11, 5,
"Error : No proxy server name");
523 "Creating credential cache for " << principal_name);
525 debugs(11, 5,
"Creating credential cache");
526 rc = krb5_create_cache(
NULL, principal_name);
528 debugs(11, 5,
"Error : Failed to create Kerberos cache");
534 service.value = (
void *)
xmalloc(strlen(
"HTTP") + strlen(proxy) + 2);
535 snprintf((
char *) service.value, strlen(
"HTTP") + strlen(proxy) + 2,
536 "%s@%s",
"HTTP", proxy);
537 service.length = strlen((
char *) service.value);
539 debugs(11, 5,
"Import gss name");
540 major_status = gss_import_name(&minor_status, &service,
543 if (
check_gss_err(major_status, minor_status,
"gss_import_name()"))
546 debugs(11, 5,
"Initialize gss security context");
547 major_status = gss_init_sec_context(&minor_status,
554 GSS_C_NO_CHANNEL_BINDINGS,
557 if (
check_gss_err(major_status, minor_status,
"gss_init_sec_context()"))
560 debugs(11, 5,
"Got token with length " << output_token.length);
561 if (output_token.length) {
562 static char b64buf[8192];
565 size_t blen =
base64_encode_update(&ctx, b64buf, output_token.length,
reinterpret_cast<const uint8_t*
>(output_token.value));
569 token =
reinterpret_cast<char*
>(b64buf);
573 gss_delete_sec_context(&minor_status, &gss_context,
NULL);
574 gss_release_buffer(&minor_status, &service);
575 gss_release_buffer(&minor_status, &input_token);
576 gss_release_buffer(&minor_status, &output_token);
577 gss_release_name(&minor_status, &server_name);