]>
Commit | Line | Data |
---|---|---|
9ca29d23 | 1 | /* |
1f7b830e | 2 | * Copyright (C) 1996-2025 The Squid Software Foundation and contributors |
9ca29d23 | 3 | * |
282b7328 AJ |
4 | * Squid software is distributed under GPLv2+ license and includes |
5 | * contributions from numerous individuals and organizations. | |
6 | * Please see the COPYING and CONTRIBUTORS files for details. | |
9ca29d23 | 7 | */ |
282b7328 | 8 | |
9ca29d23 | 9 | /* |
282b7328 | 10 | * DEBUG: 11 Hypertext Transfer Protocol (HTTP) |
9ca29d23 AJ |
11 | */ |
12 | ||
f7f3304a | 13 | #include "squid.h" |
6ff204fc | 14 | |
8b082ed9 | 15 | #if HAVE_AUTH_MODULE_NEGOTIATE && HAVE_KRB5 && HAVE_GSSAPI |
a1fe225c | 16 | #include "base64.h" |
42557148 | 17 | #include "compat/krb5.h" |
675b8408 | 18 | #include "debug/Stream.h" |
6ff204fc | 19 | #include "peer_proxy_negotiate_auth.h" |
9ca29d23 | 20 | |
9ca29d23 AJ |
21 | #if HAVE_PROFILE_H |
22 | #include <profile.h> | |
f53969cc | 23 | #endif /* HAVE_PROFILE_H */ |
42557148 | 24 | #if HAVE_ET_COM_ERR_H && !HAVE_KRB5_H |
cf99ae25 AJ |
25 | #include <et/com_err.h> |
26 | #endif /* HAVE_COM_ERR_H */ | |
9ca29d23 AJ |
27 | #if HAVE_COM_ERR_H |
28 | #include <com_err.h> | |
f53969cc | 29 | #endif /* HAVE_COM_ERR_H */ |
adcdd245 AJ |
30 | #if HAVE_GSS_H |
31 | #include <gss.h> | |
32 | #endif | |
33 | #if USE_APPLE_KRB5 | |
34 | #define GSSKRB_APPLE_DEPRECATED(x) | |
35 | #endif | |
9ca29d23 AJ |
36 | #if HAVE_GSSAPI_GSSAPI_H |
37 | #include <gssapi/gssapi.h> | |
38 | #elif HAVE_GSSAPI_H | |
39 | #include <gssapi.h> | |
f53969cc | 40 | #endif /* HAVE_GSSAPI_H */ |
9ca29d23 AJ |
41 | #if HAVE_GSSAPI_GSSAPI_EXT_H |
42 | #include <gssapi/gssapi_ext.h> | |
f53969cc | 43 | #endif /* HAVE_GSSAPI_GSSAPI_EXT_H */ |
9ca29d23 AJ |
44 | #if HAVE_GSSAPI_GSSAPI_KRB5_H |
45 | #include <gssapi/gssapi_krb5.h> | |
f53969cc | 46 | #endif /* HAVE_GSSAPI_GSSAPI_KRB5_H */ |
9ca29d23 AJ |
47 | #if HAVE_GSSAPI_GSSAPI_GENERIC_H |
48 | #include <gssapi/gssapi_generic.h> | |
f53969cc | 49 | #endif /* HAVE_GSSAPI_GSSAPI_GENERIC_H */ |
9ca29d23 AJ |
50 | |
51 | #ifndef gss_nt_service_name | |
52 | #define gss_nt_service_name GSS_C_NT_HOSTBASED_SERVICE | |
53 | #endif | |
54 | ||
b2bdde72 | 55 | #if !HAVE_ERROR_MESSAGE && HAVE_KRB5_GET_ERROR_MESSAGE |
cf99ae25 | 56 | #define error_message(code) krb5_get_error_message(kparam.context,code) |
b2bdde72 | 57 | #elif !HAVE_ERROR_MESSAGE && HAVE_KRB5_GET_ERR_TEXT |
1a22a39e | 58 | #define error_message(code) krb5_get_err_text(kparam.context,code) |
cf99ae25 | 59 | #elif !HAVE_ERROR_MESSAGE |
f53969cc SM |
60 | static char err_code[17]; |
61 | const char *KRB5_CALLCONV | |
62 | error_message(long code) { | |
63 | snprintf(err_code,16,"%ld",code); | |
64 | return err_code; | |
65 | } | |
9ca29d23 AJ |
66 | #endif |
67 | ||
68 | #ifndef gss_mech_spnego | |
f53969cc SM |
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; | |
9ca29d23 AJ |
72 | #endif |
73 | ||
1a22a39e | 74 | #if USE_IBM_KERBEROS |
9ca29d23 | 75 | #include <ibm_svc/krb5_svc.h> |
f53969cc | 76 | const char *KRB5_CALLCONV error_message(long code) { |
a1b1756c | 77 | char *msg = nullptr; |
f53969cc SM |
78 | krb5_svc_get_msg(code, &msg); |
79 | return msg; | |
80 | } | |
9ca29d23 AJ |
81 | #endif |
82 | ||
f53969cc SM |
83 | /* |
84 | * Kerberos context and cache structure | |
85 | * Caches authentication details to reduce | |
86 | * number of authentication requests to kdc | |
87 | */ | |
88 | static struct kstruct { | |
89 | krb5_context context; | |
90 | krb5_ccache cc; | |
91 | } kparam = { | |
aee3523a | 92 | nullptr, nullptr |
f53969cc | 93 | }; |
f54f527e | 94 | |
f53969cc SM |
95 | /* |
96 | * krb5_create_cache creates a Kerberos file credential cache or a memory | |
97 | * credential cache if supported. The initial key for the principal | |
98 | * principal_name is extracted from the keytab keytab_filename. | |
99 | * | |
100 | * If keytab_filename is NULL the default will be used. | |
101 | * If principal_name is NULL the first working entry of the keytab will be used. | |
102 | */ | |
103 | int krb5_create_cache(char *keytab_filename, char *principal_name); | |
9ca29d23 | 104 | |
f53969cc SM |
105 | /* |
106 | * krb5_cleanup clears used Keberos memory | |
107 | */ | |
108 | void krb5_cleanup(void); | |
9ca29d23 | 109 | |
f53969cc SM |
110 | /* |
111 | * check_gss_err checks for gssapi error codes, extracts the error message | |
112 | * and prints it. | |
113 | */ | |
114 | int check_gss_err(OM_uint32 major_status, OM_uint32 minor_status, | |
115 | const char *function); | |
116 | ||
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; | |
123 | char buf[1024]; | |
124 | size_t len; | |
125 | ||
126 | len = 0; | |
127 | msg_ctx = 0; | |
128 | while (!msg_ctx) { | |
129 | /* convert major status code (GSS-API error) to text */ | |
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; | |
f54f527e AJ |
137 | } |
138 | gss_release_buffer(&min_stat, &status_string); | |
f53969cc | 139 | break; |
f54f527e | 140 | } |
f53969cc SM |
141 | gss_release_buffer(&min_stat, &status_string); |
142 | } | |
143 | if (sizeof(buf) > len + 2) { | |
144 | strcpy(buf + len, ". "); | |
145 | len += 2; | |
146 | } | |
147 | msg_ctx = 0; | |
148 | while (!msg_ctx) { | |
149 | /* convert minor status code (underlying routine error) to text */ | |
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; | |
f54f527e AJ |
157 | } |
158 | gss_release_buffer(&min_stat, &status_string); | |
f53969cc | 159 | break; |
f54f527e | 160 | } |
f53969cc | 161 | gss_release_buffer(&min_stat, &status_string); |
f54f527e | 162 | } |
bf95c10a | 163 | debugs(11, 5, function << "failed: " << buf); |
f53969cc | 164 | return (1); |
9ca29d23 | 165 | } |
f53969cc SM |
166 | return (0); |
167 | } | |
9ca29d23 | 168 | |
f53969cc | 169 | void krb5_cleanup() { |
bf95c10a | 170 | debugs(11, 5, "Cleanup kerberos context"); |
f53969cc SM |
171 | if (kparam.context) { |
172 | if (kparam.cc) | |
173 | krb5_cc_destroy(kparam.context, kparam.cc); | |
aee3523a | 174 | kparam.cc = nullptr; |
f53969cc | 175 | krb5_free_context(kparam.context); |
aee3523a | 176 | kparam.context = nullptr; |
9ca29d23 | 177 | } |
f53969cc | 178 | } |
9ca29d23 | 179 | |
f53969cc | 180 | int krb5_create_cache(char *kf, char *pn) { |
9ca29d23 AJ |
181 | |
182 | #define KT_PATH_MAX 256 | |
183 | #define MAX_RENEW_TIME "365d" | |
184 | #define DEFAULT_SKEW (krb5_deltat) 600 | |
185 | ||
aee3523a AR |
186 | static char *keytab_filename = nullptr, *principal_name = nullptr; |
187 | static krb5_keytab keytab = nullptr; | |
f53969cc SM |
188 | static krb5_keytab_entry entry; |
189 | static krb5_kt_cursor cursor; | |
aee3523a | 190 | static krb5_creds *creds = nullptr; |
adcdd245 | 191 | #if HAVE_LIBHEIMDAL_KRB5 && !HAVE_KRB5_GET_RENEWED_CREDS |
f53969cc | 192 | static krb5_creds creds2; |
9ca29d23 | 193 | #endif |
aee3523a | 194 | static krb5_principal principal = nullptr; |
f53969cc | 195 | static krb5_deltat skew; |
f54f527e | 196 | |
4ebcf1ce | 197 | #if HAVE_KRB5_GET_INIT_CREDS_OPT_ALLOC |
f53969cc | 198 | krb5_get_init_creds_opt *options; |
4ebcf1ce | 199 | #else |
f53969cc | 200 | krb5_get_init_creds_opt options; |
4ebcf1ce | 201 | #endif |
f53969cc SM |
202 | krb5_error_code code = 0; |
203 | krb5_deltat rlife; | |
f54f527e | 204 | #if HAVE_PROFILE_H && HAVE_KRB5_GET_PROFILE && HAVE_PROFILE_GET_INTEGER && HAVE_PROFILE_RELEASE |
f53969cc | 205 | profile_t profile; |
9ca29d23 | 206 | #endif |
adcdd245 | 207 | #if HAVE_LIBHEIMDAL_KRB5 && !HAVE_KRB5_GET_RENEWED_CREDS |
f53969cc | 208 | krb5_kdc_flags flags; |
4ebcf1ce | 209 | #if HAVE_KRB5_PRINCIPAL_GET_REALM |
f53969cc | 210 | const char *client_realm; |
4ebcf1ce | 211 | #else |
f53969cc | 212 | krb5_realm client_realm; |
4ebcf1ce | 213 | #endif |
9ca29d23 | 214 | #endif |
f53969cc | 215 | char *mem_cache; |
f54f527e AJ |
216 | |
217 | restart: | |
f53969cc SM |
218 | /* |
219 | * Check if credentials need to be renewed | |
220 | */ | |
221 | if (creds && | |
aee3523a AR |
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) { | |
4ebcf1ce | 225 | #if HAVE_KRB5_GET_RENEWED_CREDS |
f53969cc SM |
226 | /* renew ticket */ |
227 | code = | |
228 | krb5_get_renewed_creds(kparam.context, creds, principal, | |
aee3523a | 229 | kparam.cc, nullptr); |
9ca29d23 | 230 | #else |
f53969cc SM |
231 | /* renew ticket */ |
232 | flags.i = 0; | |
233 | flags.b.renewable = flags.b.renew = 1; | |
234 | ||
235 | code = | |
236 | krb5_cc_get_principal(kparam.context, kparam.cc, | |
237 | &creds2.client); | |
238 | if (code) { | |
239 | debugs(11, 5, | |
bf95c10a | 240 | |
f53969cc SM |
241 | "Error while getting principal from credential cache : " |
242 | << error_message(code)); | |
243 | return (1); | |
244 | } | |
4ebcf1ce | 245 | #if HAVE_KRB5_PRINCIPAL_GET_REALM |
f53969cc | 246 | client_realm = krb5_principal_get_realm(kparam.context, principal); |
4ebcf1ce | 247 | #else |
f53969cc | 248 | client_realm = krb5_princ_realm(kparam.context, creds2.client); |
9ca29d23 | 249 | #endif |
f53969cc SM |
250 | code = |
251 | krb5_make_principal(kparam.context, &creds2.server, | |
252 | (krb5_const_realm)&client_realm, KRB5_TGS_NAME, | |
a1b1756c | 253 | (krb5_const_realm)&client_realm, nullptr); |
f54f527e | 254 | if (code) { |
f54f527e | 255 | debugs(11, 5, |
bf95c10a | 256 | "Error while getting krbtgt principal : " << |
f54f527e AJ |
257 | error_message(code)); |
258 | return (1); | |
259 | } | |
260 | code = | |
a1b1756c AJ |
261 | krb5_get_kdc_cred(kparam.context, kparam.cc, flags, nullptr, |
262 | nullptr, &creds2, &creds); | |
f53969cc SM |
263 | krb5_free_creds(kparam.context, &creds2); |
264 | #endif | |
f54f527e | 265 | if (code) { |
f53969cc SM |
266 | if (code == KRB5KRB_AP_ERR_TKT_EXPIRED) { |
267 | krb5_free_creds(kparam.context, creds); | |
aee3523a | 268 | creds = nullptr; |
f53969cc SM |
269 | /* this can happen because of clock skew */ |
270 | goto restart; | |
271 | } | |
f54f527e | 272 | debugs(11, 5, |
bf95c10a | 273 | "Error while get credentials : " << |
f54f527e AJ |
274 | error_message(code)); |
275 | return (1); | |
276 | } | |
f53969cc SM |
277 | } |
278 | } else { | |
279 | /* reinit */ | |
280 | if (!kparam.context) { | |
281 | code = krb5_init_context(&kparam.context); | |
f54f527e AJ |
282 | if (code) { |
283 | debugs(11, 5, | |
bf95c10a | 284 | "Error while initialising Kerberos library : " |
f53969cc | 285 | << error_message(code)); |
f54f527e AJ |
286 | return (1); |
287 | } | |
f53969cc SM |
288 | } |
289 | #if HAVE_PROFILE_H && HAVE_KRB5_GET_PROFILE && HAVE_PROFILE_GET_INTEGER && HAVE_PROFILE_RELEASE | |
290 | code = krb5_get_profile(kparam.context, &profile); | |
291 | if (code) { | |
292 | if (profile) | |
293 | profile_release(profile); | |
294 | debugs(11, 5, | |
bf95c10a | 295 | "Error while getting profile : " << |
f53969cc SM |
296 | error_message(code)); |
297 | return (1); | |
298 | } | |
299 | code = | |
aee3523a | 300 | profile_get_integer(profile, "libdefaults", "clockskew", nullptr, |
f53969cc SM |
301 | 5 * 60, &skew); |
302 | if (profile) | |
303 | profile_release(profile); | |
304 | if (code) { | |
305 | debugs(11, 5, | |
bf95c10a | 306 | "Error while getting clockskew : " << |
f53969cc SM |
307 | error_message(code)); |
308 | return (1); | |
309 | } | |
202d3a22 | 310 | #elif HAVE_LIBHEIMDAL_KRB5 |
f53969cc | 311 | skew = krb5_get_max_time_skew(kparam.context); |
9ca29d23 | 312 | #else |
f53969cc | 313 | skew = DEFAULT_SKEW; |
9ca29d23 | 314 | #endif |
f54f527e | 315 | |
f53969cc SM |
316 | if (!kf) { |
317 | char buf[KT_PATH_MAX], *p; | |
f54f527e | 318 | |
f53969cc SM |
319 | krb5_kt_default_name(kparam.context, buf, KT_PATH_MAX); |
320 | p = strchr(buf, ':'); | |
321 | if (p) | |
322 | ++p; | |
323 | xfree(keytab_filename); | |
324 | keytab_filename = xstrdup(p ? p : buf); | |
325 | } else { | |
326 | keytab_filename = xstrdup(kf); | |
327 | } | |
f54f527e | 328 | |
f53969cc SM |
329 | code = krb5_kt_resolve(kparam.context, keytab_filename, &keytab); |
330 | if (code) { | |
331 | debugs(11, 5, | |
bf95c10a | 332 | "Error while resolving keytab filename " << |
f53969cc SM |
333 | keytab_filename << " : " << error_message(code)); |
334 | return (1); | |
335 | } | |
336 | ||
337 | if (!pn) { | |
338 | code = krb5_kt_start_seq_get(kparam.context, keytab, &cursor); | |
339 | if (code) { | |
f54f527e | 340 | debugs(11, 5, |
bf95c10a | 341 | "Error while starting keytab scan : " << |
f53969cc | 342 | error_message(code)); |
f54f527e AJ |
343 | return (1); |
344 | } | |
f54f527e | 345 | code = |
f53969cc SM |
346 | krb5_kt_next_entry(kparam.context, keytab, &entry, &cursor); |
347 | krb5_copy_principal(kparam.context, entry.principal, | |
348 | &principal); | |
349 | if (code && code != KRB5_KT_END) { | |
f54f527e | 350 | debugs(11, 5, |
bf95c10a | 351 | "Error while scanning keytab : " << |
f54f527e AJ |
352 | error_message(code)); |
353 | return (1); | |
354 | } | |
9ca29d23 | 355 | |
f53969cc | 356 | code = krb5_kt_end_seq_get(kparam.context, keytab, &cursor); |
f54f527e AJ |
357 | if (code) { |
358 | debugs(11, 5, | |
bf95c10a | 359 | "Error while ending keytab scan : " << |
f53969cc | 360 | error_message(code)); |
f54f527e AJ |
361 | return (1); |
362 | } | |
adcdd245 | 363 | #if HAVE_LIBHEIMDAL_KRB5 || ( HAVE_KRB5_KT_FREE_ENTRY && HAVE_DECL_KRB5_KT_FREE_ENTRY) |
f53969cc SM |
364 | code = krb5_kt_free_entry(kparam.context, &entry); |
365 | #else | |
366 | code = krb5_free_keytab_entry_contents(kparam.context, &entry); | |
367 | #endif | |
f54f527e AJ |
368 | if (code) { |
369 | debugs(11, 5, | |
bf95c10a | 370 | "Error while freeing keytab entry : " << |
f54f527e AJ |
371 | error_message(code)); |
372 | return (1); | |
373 | } | |
f53969cc SM |
374 | |
375 | } else { | |
376 | principal_name = xstrdup(pn); | |
377 | } | |
378 | ||
379 | if (!principal) { | |
380 | code = | |
381 | krb5_parse_name(kparam.context, principal_name, &principal); | |
f54f527e AJ |
382 | if (code) { |
383 | debugs(11, 5, | |
bf95c10a | 384 | "Error while parsing principal name " << |
f53969cc | 385 | principal_name << " : " << error_message(code)); |
f54f527e AJ |
386 | return (1); |
387 | } | |
f54f527e | 388 | } |
9ca29d23 | 389 | |
f53969cc SM |
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); | |
394 | #else | |
395 | krb5_get_init_creds_opt_init(&options); | |
396 | #endif | |
397 | code = krb5_string_to_deltat((char *) MAX_RENEW_TIME, &rlife); | |
398 | if (code != 0 || rlife == 0) { | |
399 | debugs(11, 5, | |
bf95c10a | 400 | "Error bad lifetime value " << MAX_RENEW_TIME << |
f53969cc SM |
401 | " : " << error_message(code)); |
402 | return (1); | |
f54f527e | 403 | } |
f53969cc SM |
404 | #if HAVE_KRB5_GET_INIT_CREDS_OPT_ALLOC |
405 | krb5_get_init_creds_opt_set_renew_life(options, rlife); | |
406 | code = | |
407 | krb5_get_init_creds_keytab(kparam.context, creds, principal, | |
aee3523a | 408 | keytab, 0, nullptr, options); |
f53969cc SM |
409 | #if HAVE_KRB5_GET_INIT_CREDS_FREE_CONTEXT |
410 | krb5_get_init_creds_opt_free(kparam.context, options); | |
411 | #else | |
412 | krb5_get_init_creds_opt_free(options); | |
413 | #endif | |
414 | #else | |
415 | krb5_get_init_creds_opt_set_renew_life(&options, rlife); | |
416 | code = | |
417 | krb5_get_init_creds_keytab(kparam.context, creds, principal, | |
a1b1756c | 418 | keytab, 0, nullptr, &options); |
f53969cc SM |
419 | #endif |
420 | if (code) { | |
f54f527e | 421 | debugs(11, 5, |
bf95c10a | 422 | |
f53969cc SM |
423 | "Error while initializing credentials from keytab : " << |
424 | error_message(code)); | |
425 | return (1); | |
426 | } | |
427 | #if !HAVE_KRB5_MEMORY_CACHE | |
428 | mem_cache = | |
429 | (char *) xmalloc(strlen("FILE:/tmp/peer_proxy_negotiate_auth_") | |
430 | + 16); | |
431 | if (!mem_cache) { | |
432 | debugs(11, 5, "Error while allocating memory"); | |
433 | return(1); | |
f54f527e | 434 | } |
f53969cc SM |
435 | snprintf(mem_cache, |
436 | strlen("FILE:/tmp/peer_proxy_negotiate_auth_") + 16, | |
437 | "FILE:/tmp/peer_proxy_negotiate_auth_%d", (int) getpid()); | |
438 | #else | |
439 | mem_cache = | |
440 | (char *) xmalloc(strlen("MEMORY:peer_proxy_negotiate_auth_") + | |
441 | 16); | |
442 | if (!mem_cache) { | |
443 | debugs(11, 5, "Error while allocating memory"); | |
444 | return(1); | |
445 | } | |
446 | snprintf(mem_cache, | |
447 | strlen("MEMORY:peer_proxy_negotiate_auth_") + 16, | |
448 | "MEMORY:peer_proxy_negotiate_auth_%d", (int) getpid()); | |
449 | #endif | |
f54f527e | 450 | |
f53969cc SM |
451 | setenv("KRB5CCNAME", mem_cache, 1); |
452 | code = krb5_cc_resolve(kparam.context, mem_cache, &kparam.cc); | |
453 | xfree(mem_cache); | |
454 | if (code) { | |
455 | debugs(11, 5, | |
bf95c10a | 456 | "Error while resolving memory credential cache : " |
f53969cc SM |
457 | << error_message(code)); |
458 | return (1); | |
459 | } | |
460 | code = krb5_cc_initialize(kparam.context, kparam.cc, principal); | |
461 | if (code) { | |
462 | debugs(11, 5, | |
bf95c10a | 463 | |
f53969cc SM |
464 | "Error while initializing memory credential cache : " << |
465 | error_message(code)); | |
466 | return (1); | |
467 | } | |
468 | code = krb5_cc_store_cred(kparam.context, kparam.cc, creds); | |
469 | if (code) { | |
470 | debugs(11, 5, | |
bf95c10a | 471 | "Error while storing credentials : " << |
f53969cc SM |
472 | error_message(code)); |
473 | return (1); | |
f54f527e AJ |
474 | } |
475 | ||
f53969cc SM |
476 | if (!creds->times.starttime) |
477 | creds->times.starttime = creds->times.authtime; | |
478 | } | |
479 | return (0); | |
480 | } | |
481 | ||
482 | /* | |
483 | * peer_proxy_negotiate_auth gets a GSSAPI token for principal_name | |
484 | * and base64 encodes it. | |
485 | */ | |
16b17ba9 | 486 | char *peer_proxy_negotiate_auth(char *principal_name, const char * const proxy, int flags) { |
f53969cc SM |
487 | int rc = 0; |
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; | |
aee3523a | 494 | char *token = nullptr; |
f53969cc | 495 | |
aee3523a AR |
496 | setbuf(stdout, nullptr); |
497 | setbuf(stdin, nullptr); | |
f53969cc SM |
498 | |
499 | if (!proxy) { | |
bf95c10a | 500 | debugs(11, 5, "Error : No proxy server name"); |
aee3523a | 501 | return nullptr; |
f53969cc SM |
502 | } |
503 | ||
9825b398 AJ |
504 | if (!(flags & PEER_PROXY_NEGOTIATE_NOKEYTAB)) { |
505 | if (principal_name) | |
506 | debugs(11, 5, | |
bf95c10a | 507 | "Creating credential cache for " << principal_name); |
9825b398 | 508 | else |
bf95c10a | 509 | debugs(11, 5, "Creating credential cache"); |
aee3523a | 510 | rc = krb5_create_cache(nullptr, principal_name); |
9825b398 | 511 | if (rc) { |
bf95c10a | 512 | debugs(11, 5, "Error : Failed to create Kerberos cache"); |
9825b398 | 513 | krb5_cleanup(); |
aee3523a | 514 | return nullptr; |
9825b398 | 515 | } |
f53969cc | 516 | } |
f54f527e | 517 | |
f53969cc SM |
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); | |
522 | ||
bf95c10a | 523 | debugs(11, 5, "Import gss name"); |
f53969cc SM |
524 | major_status = gss_import_name(&minor_status, &service, |
525 | gss_nt_service_name, &server_name); | |
526 | ||
527 | if (check_gss_err(major_status, minor_status, "gss_import_name()")) | |
528 | goto cleanup; | |
529 | ||
bf95c10a | 530 | debugs(11, 5, "Initialize gss security context"); |
f53969cc SM |
531 | major_status = gss_init_sec_context(&minor_status, |
532 | GSS_C_NO_CREDENTIAL, | |
533 | &gss_context, | |
534 | server_name, | |
535 | gss_mech_spnego, | |
536 | 0, | |
537 | 0, | |
538 | GSS_C_NO_CHANNEL_BINDINGS, | |
aee3523a | 539 | &input_token, nullptr, &output_token, nullptr, nullptr); |
f53969cc SM |
540 | |
541 | if (check_gss_err(major_status, minor_status, "gss_init_sec_context()")) | |
542 | goto cleanup; | |
543 | ||
bf95c10a | 544 | debugs(11, 5, "Got token with length " << output_token.length); |
f53969cc | 545 | if (output_token.length) { |
1d11e9b3 | 546 | static char b64buf[8192]; // XXX: 8KB only because base64_encode_bin() used to. |
aadbbd7d AJ |
547 | struct base64_encode_ctx ctx; |
548 | base64_encode_init(&ctx); | |
549 | size_t blen = base64_encode_update(&ctx, b64buf, output_token.length, reinterpret_cast<const uint8_t*>(output_token.value)); | |
550 | blen += base64_encode_final(&ctx, b64buf+blen); | |
551 | b64buf[blen] = '\0'; | |
552 | ||
553 | token = reinterpret_cast<char*>(b64buf); | |
9ca29d23 AJ |
554 | } |
555 | ||
f53969cc | 556 | cleanup: |
aee3523a | 557 | gss_delete_sec_context(&minor_status, &gss_context, nullptr); |
f53969cc SM |
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); | |
562 | ||
563 | return token; | |
564 | } | |
565 | ||
8b082ed9 | 566 | #endif /* HAVE_AUTH_MODULE_NEGOTIATE && HAVE_KRB5 && HAVE_GSSAPI */ |
f53969cc | 567 |