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