15 #if HAVE_AUTH_MODULE_NEGOTIATE && HAVE_KRB5 && HAVE_GSSAPI
24 #if HAVE_ET_COM_ERR_H && !HAVE_KRB5_H
25 #include <et/com_err.h>
34 #define GSSKRB_APPLE_DEPRECATED(x)
36 #if HAVE_GSSAPI_GSSAPI_H
37 #include <gssapi/gssapi.h>
41 #if HAVE_GSSAPI_GSSAPI_EXT_H
42 #include <gssapi/gssapi_ext.h>
44 #if HAVE_GSSAPI_GSSAPI_KRB5_H
45 #include <gssapi/gssapi_krb5.h>
47 #if HAVE_GSSAPI_GSSAPI_GENERIC_H
48 #include <gssapi/gssapi_generic.h>
51 #ifndef gss_nt_service_name
52 #define gss_nt_service_name GSS_C_NT_HOSTBASED_SERVICE
55 #if !HAVE_ERROR_MESSAGE && HAVE_KRB5_GET_ERROR_MESSAGE
56 #define error_message(code) krb5_get_error_message(kparam.context,code)
57 #elif !HAVE_ERROR_MESSAGE && HAVE_KRB5_GET_ERR_TEXT
58 #define error_message(code) krb5_get_err_text(kparam.context,code)
59 #elif !HAVE_ERROR_MESSAGE
60 static char err_code[17];
61 const char *KRB5_CALLCONV
62 error_message(
long code) {
63 snprintf(err_code,16,
"%ld",
code);
68 #ifndef gss_mech_spnego
69 static gss_OID_desc _gss_mech_spnego =
70 { 6, (
void *)
"\x2b\x06\x01\x05\x05\x02" };
71 gss_OID gss_mech_spnego = &_gss_mech_spnego;
75 #include <ibm_svc/krb5_svc.h>
76 const char *KRB5_CALLCONV error_message(
long code) {
78 krb5_svc_get_msg(
code, &msg);
88 static struct kstruct {
103 int krb5_create_cache(
char *keytab_filename,
char *principal_name);
108 void krb5_cleanup(
void);
114 int check_gss_err(OM_uint32 major_status, OM_uint32 minor_status,
115 const char *
function);
117 int check_gss_err(OM_uint32 major_status, OM_uint32 minor_status,
118 const char *
function) {
119 if (GSS_ERROR(major_status)) {
120 OM_uint32 maj_stat, min_stat;
121 OM_uint32 msg_ctx = 0;
122 gss_buffer_desc status_string;
130 maj_stat = gss_display_status(&min_stat, major_status,
131 GSS_C_GSS_CODE, GSS_C_NULL_OID, &msg_ctx, &status_string);
132 if (maj_stat == GSS_S_COMPLETE) {
133 if (
sizeof(buf) > len + status_string.length + 1) {
134 memcpy(buf + len, status_string.value,
135 status_string.length);
136 len += status_string.length;
138 gss_release_buffer(&min_stat, &status_string);
141 gss_release_buffer(&min_stat, &status_string);
143 if (
sizeof(buf) > len + 2) {
144 strcpy(buf + len,
". ");
150 maj_stat = gss_display_status(&min_stat, minor_status,
151 GSS_C_MECH_CODE, GSS_C_NULL_OID, &msg_ctx, &status_string);
152 if (maj_stat == GSS_S_COMPLETE) {
153 if (
sizeof(buf) > len + status_string.length) {
154 memcpy(buf + len, status_string.value,
155 status_string.length);
156 len += status_string.length;
158 gss_release_buffer(&min_stat, &status_string);
161 gss_release_buffer(&min_stat, &status_string);
163 debugs(11, 5,
function <<
"failed: " << buf);
169 void krb5_cleanup() {
170 debugs(11, 5,
"Cleanup kerberos context");
171 if (kparam.context) {
173 krb5_cc_destroy(kparam.context, kparam.cc);
175 krb5_free_context(kparam.context);
176 kparam.context =
nullptr;
180 int krb5_create_cache(
char *kf,
char *pn) {
182 #define KT_PATH_MAX 256
183 #define MAX_RENEW_TIME "365d"
184 #define DEFAULT_SKEW (krb5_deltat) 600
186 static char *keytab_filename =
nullptr, *principal_name =
nullptr;
187 static krb5_keytab keytab =
nullptr;
188 static krb5_keytab_entry entry;
189 static krb5_kt_cursor cursor;
190 static krb5_creds *creds =
nullptr;
191 #if HAVE_LIBHEIMDAL_KRB5 && !HAVE_KRB5_GET_RENEWED_CREDS
192 static krb5_creds creds2;
194 static krb5_principal principal =
nullptr;
195 static krb5_deltat skew;
197 #if HAVE_KRB5_GET_INIT_CREDS_OPT_ALLOC
198 krb5_get_init_creds_opt *options;
200 krb5_get_init_creds_opt options;
202 krb5_error_code
code = 0;
204 #if HAVE_PROFILE_H && HAVE_KRB5_GET_PROFILE && HAVE_PROFILE_GET_INTEGER && HAVE_PROFILE_RELEASE
207 #if HAVE_LIBHEIMDAL_KRB5 && !HAVE_KRB5_GET_RENEWED_CREDS
208 krb5_kdc_flags flags;
209 #if HAVE_KRB5_PRINCIPAL_GET_REALM
210 const char *client_realm;
212 krb5_realm client_realm;
222 (creds->times.endtime - time(
nullptr) > skew) &&
223 (creds->times.renew_till - time(
nullptr) > 2 * skew)) {
224 if (creds->times.endtime - time(
nullptr) < 2 * skew) {
225 #if HAVE_KRB5_GET_RENEWED_CREDS
228 krb5_get_renewed_creds(kparam.context, creds, principal,
233 flags.b.renewable = flags.b.renew = 1;
236 krb5_cc_get_principal(kparam.context, kparam.cc,
241 "Error while getting principal from credential cache : "
242 << error_message(
code));
245 #if HAVE_KRB5_PRINCIPAL_GET_REALM
246 client_realm = krb5_principal_get_realm(kparam.context, principal);
248 client_realm = krb5_princ_realm(kparam.context, creds2.client);
251 krb5_make_principal(kparam.context, &creds2.server,
252 (krb5_const_realm)&client_realm, KRB5_TGS_NAME,
253 (krb5_const_realm)&client_realm,
nullptr);
256 "Error while getting krbtgt principal : " <<
257 error_message(
code));
261 krb5_get_kdc_cred(kparam.context, kparam.cc, flags,
nullptr,
262 nullptr, &creds2, &creds);
263 krb5_free_creds(kparam.context, &creds2);
266 if (
code == KRB5KRB_AP_ERR_TKT_EXPIRED) {
267 krb5_free_creds(kparam.context, creds);
273 "Error while get credentials : " <<
274 error_message(
code));
280 if (!kparam.context) {
281 code = krb5_init_context(&kparam.context);
284 "Error while initialising Kerberos library : "
285 << error_message(
code));
289 #if HAVE_PROFILE_H && HAVE_KRB5_GET_PROFILE && HAVE_PROFILE_GET_INTEGER && HAVE_PROFILE_RELEASE
290 code = krb5_get_profile(kparam.context, &profile);
293 profile_release(profile);
295 "Error while getting profile : " <<
296 error_message(
code));
300 profile_get_integer(profile,
"libdefaults",
"clockskew",
nullptr,
303 profile_release(profile);
306 "Error while getting clockskew : " <<
307 error_message(
code));
310 #elif HAVE_LIBHEIMDAL_KRB5
311 skew = krb5_get_max_time_skew(kparam.context);
317 char buf[KT_PATH_MAX], *p;
319 krb5_kt_default_name(kparam.context, buf, KT_PATH_MAX);
320 p = strchr(buf,
':');
323 xfree(keytab_filename);
324 keytab_filename =
xstrdup(p ? p : buf);
329 code = krb5_kt_resolve(kparam.context, keytab_filename, &keytab);
332 "Error while resolving keytab filename " <<
333 keytab_filename <<
" : " << error_message(
code));
338 code = krb5_kt_start_seq_get(kparam.context, keytab, &cursor);
341 "Error while starting keytab scan : " <<
342 error_message(
code));
346 krb5_kt_next_entry(kparam.context, keytab, &entry, &cursor);
347 krb5_copy_principal(kparam.context, entry.principal,
351 "Error while scanning keytab : " <<
352 error_message(
code));
356 code = krb5_kt_end_seq_get(kparam.context, keytab, &cursor);
359 "Error while ending keytab scan : " <<
360 error_message(
code));
363 #if HAVE_LIBHEIMDAL_KRB5 || ( HAVE_KRB5_KT_FREE_ENTRY && HAVE_DECL_KRB5_KT_FREE_ENTRY)
364 code = krb5_kt_free_entry(kparam.context, &entry);
366 code = krb5_free_keytab_entry_contents(kparam.context, &entry);
370 "Error while freeing keytab entry : " <<
371 error_message(
code));
381 krb5_parse_name(kparam.context, principal_name, &principal);
384 "Error while parsing principal name " <<
385 principal_name <<
" : " << error_message(
code));
390 creds = (krb5_creds *)
xmalloc(
sizeof(*creds));
391 memset(creds, 0,
sizeof(*creds));
392 #if HAVE_KRB5_GET_INIT_CREDS_OPT_ALLOC
393 krb5_get_init_creds_opt_alloc(kparam.context, &options);
395 krb5_get_init_creds_opt_init(&options);
397 code = krb5_string_to_deltat((
char *) MAX_RENEW_TIME, &rlife);
398 if (
code != 0 || rlife == 0) {
400 "Error bad lifetime value " << MAX_RENEW_TIME <<
401 " : " << error_message(
code));
404 #if HAVE_KRB5_GET_INIT_CREDS_OPT_ALLOC
405 krb5_get_init_creds_opt_set_renew_life(options, rlife);
407 krb5_get_init_creds_keytab(kparam.context, creds, principal,
408 keytab, 0,
nullptr, options);
409 #if HAVE_KRB5_GET_INIT_CREDS_FREE_CONTEXT
410 krb5_get_init_creds_opt_free(kparam.context, options);
412 krb5_get_init_creds_opt_free(options);
415 krb5_get_init_creds_opt_set_renew_life(&options, rlife);
417 krb5_get_init_creds_keytab(kparam.context, creds, principal,
418 keytab, 0,
nullptr, &options);
423 "Error while initializing credentials from keytab : " <<
424 error_message(
code));
427 #if !HAVE_KRB5_MEMORY_CACHE
429 (
char *)
xmalloc(strlen(
"FILE:/tmp/peer_proxy_negotiate_auth_")
432 debugs(11, 5,
"Error while allocating memory");
436 strlen(
"FILE:/tmp/peer_proxy_negotiate_auth_") + 16,
437 "FILE:/tmp/peer_proxy_negotiate_auth_%d", (
int) getpid());
440 (
char *)
xmalloc(strlen(
"MEMORY:peer_proxy_negotiate_auth_") +
443 debugs(11, 5,
"Error while allocating memory");
447 strlen(
"MEMORY:peer_proxy_negotiate_auth_") + 16,
448 "MEMORY:peer_proxy_negotiate_auth_%d", (
int) getpid());
451 setenv(
"KRB5CCNAME", mem_cache, 1);
452 code = krb5_cc_resolve(kparam.context, mem_cache, &kparam.cc);
456 "Error while resolving memory credential cache : "
457 << error_message(
code));
460 code = krb5_cc_initialize(kparam.context, kparam.cc, principal);
464 "Error while initializing memory credential cache : " <<
465 error_message(
code));
468 code = krb5_cc_store_cred(kparam.context, kparam.cc, creds);
471 "Error while storing credentials : " <<
472 error_message(
code));
476 if (!creds->times.starttime)
477 creds->times.starttime = creds->times.authtime;
486 char *peer_proxy_negotiate_auth(
char *principal_name,
char *proxy,
int flags) {
488 OM_uint32 major_status, minor_status;
489 gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT;
490 gss_name_t server_name = GSS_C_NO_NAME;
491 gss_buffer_desc service = GSS_C_EMPTY_BUFFER;
492 gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
493 gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
494 char *token =
nullptr;
496 setbuf(stdout,
nullptr);
497 setbuf(stdin,
nullptr);
500 debugs(11, 5,
"Error : No proxy server name");
507 "Creating credential cache for " << principal_name);
509 debugs(11, 5,
"Creating credential cache");
510 rc = krb5_create_cache(
nullptr, principal_name);
512 debugs(11, 5,
"Error : Failed to create Kerberos cache");
518 service.value = (
void *)
xmalloc(strlen(
"HTTP") + strlen(proxy) + 2);
519 snprintf((
char *) service.value, strlen(
"HTTP") + strlen(proxy) + 2,
520 "%s@%s",
"HTTP", proxy);
521 service.length = strlen((
char *) service.value);
523 debugs(11, 5,
"Import gss name");
524 major_status = gss_import_name(&minor_status, &service,
527 if (
check_gss_err(major_status, minor_status,
"gss_import_name()"))
530 debugs(11, 5,
"Initialize gss security context");
531 major_status = gss_init_sec_context(&minor_status,
538 GSS_C_NO_CHANNEL_BINDINGS,
539 &input_token,
nullptr, &output_token,
nullptr,
nullptr);
541 if (
check_gss_err(major_status, minor_status,
"gss_init_sec_context()"))
544 debugs(11, 5,
"Got token with length " << output_token.length);
545 if (output_token.length) {
546 static char b64buf[8192];
549 size_t blen =
base64_encode_update(&ctx, b64buf, output_token.length,
reinterpret_cast<const uint8_t*
>(output_token.value));
553 token =
reinterpret_cast<char*
>(b64buf);
557 gss_delete_sec_context(&minor_status, &gss_context,
nullptr);
558 gss_release_buffer(&minor_status, &service);
559 gss_release_buffer(&minor_status, &input_token);
560 gss_release_buffer(&minor_status, &output_token);
561 gss_release_name(&minor_status, &server_name);