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