Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(227)

Side by Side Diff: net/http/http_auth_gssapi_posix.cc

Issue 1736009: Preliminary support for GSSAPI (Linux and Mac OS X). (Closed) Base URL: http://src.chromium.org/svn/trunk/src/
Patch Set: Bug and style fixes. Created 10 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "net/http/http_auth_gssapi_posix.h"
6
7 #include "base/base64.h"
8 #include "base/file_path.h"
9 #include "base/logging.h"
10 #include "base/singleton.h"
11 #include "base/string_util.h"
12 #include "net/base/net_errors.h"
13 #include "net/base/net_util.h"
14
15 namespace {
16
17 gssapi::gss_OID_desc LOCAL_GSS_C_NT_HOSTBASED_SERVICE_VAL = {
18 10,
19 const_cast<char *>("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04")
20 };
21
22 gssapi::gss_OID LOCAL_GSS_C_NT_HOSTBASED_SERVICE =
23 &LOCAL_GSS_C_NT_HOSTBASED_SERVICE_VAL;
24
25 } // namespace
26
27 namespace net {
28
29 GSSAPISharedLibrary::GSSAPISharedLibrary()
30 : initialized_(false),
31 gssapi_library_(NULL),
32 import_name_(NULL),
33 release_name_(NULL),
34 release_buffer_(NULL),
35 display_status_(NULL),
36 init_sec_context_(NULL),
37 wrap_size_limit_(NULL) {
38 }
39
40 GSSAPISharedLibrary::~GSSAPISharedLibrary() {
41 if (gssapi_library_) {
42 base::UnloadNativeLibrary(gssapi_library_);
43 gssapi_library_ = NULL;
44 }
45 }
46
47 bool GSSAPISharedLibrary::Init() {
48 if (!initialized_)
49 InitImpl();
50 return initialized_;
51 }
52
53 bool GSSAPISharedLibrary::InitImpl() {
54 DCHECK(!initialized_);
55 gssapi_library_ = LoadSharedObject();
56 if (gssapi_library_ == NULL)
57 return false;
58 if (!BindMethods())
59 return false;
60 initialized_ = true;
61 return true;
62 }
63
64 base::NativeLibrary GSSAPISharedLibrary::LoadSharedObject() {
65 static const char* kLibraryNames[] = {
66 #if defined(OS_MACOSX)
67 "libgssapi_krb5.dylib" // MIT Kerberos
68 #else
69 "libgssapi_krb5.so.2", // MIT Kerberos
70 "libgssapi.so.4", // Heimdal
71 "libgssapi.so.1" // Heimdal
72 #endif
73 };
74 static size_t num_lib_names = arraysize(kLibraryNames);
75
76 for (size_t i = 0; i < num_lib_names; ++i) {
77 const char* library_name = kLibraryNames[i];
78 FilePath file_path(library_name);
79 base::NativeLibrary lib = base::LoadNativeLibrary(file_path);
80 if (lib)
81 return lib;
82 }
83 return NULL;
84 }
85
86 template <typename T>
87 bool FindAndBind(base::NativeLibrary library, const char* name, T* t) {
88 void* func = base::GetFunctionPointerFromNativeLibrary(library, name);
89 if (func == NULL)
90 return false;
91 *t = reinterpret_cast<T>(func);
92 return true;
93 }
94
95 bool GSSAPISharedLibrary::BindMethods() {
96 DCHECK(gssapi_library_ != NULL);
97 if (!FindAndBind(gssapi_library_, "gss_import_name", &import_name_))
98 return false;
99 if (!FindAndBind(gssapi_library_, "gss_release_name", &release_name_))
100 return false;
101 if (!FindAndBind(gssapi_library_, "gss_release_buffer", &release_buffer_))
102 return false;
103 if (!FindAndBind(gssapi_library_, "gss_display_status", &display_status_))
104 return false;
105 if (!FindAndBind(gssapi_library_, "gss_init_sec_context", &init_sec_context_))
106 return false;
107 if (!FindAndBind(gssapi_library_, "gss_wrap_size_limit", &wrap_size_limit_))
108 return false;
109 return true;
110 }
111
112 gssapi::OM_uint32 GSSAPISharedLibrary::import_name(
113 gssapi::OM_uint32* minor_status,
114 const gssapi::gss_buffer_t input_name_buffer,
115 const gssapi::gss_OID input_name_type,
116 gssapi::gss_name_t* output_name) {
117 DCHECK(initialized_);
118 return import_name_(minor_status, input_name_buffer, input_name_type,
119 output_name);
120 }
121
122 gssapi::OM_uint32 GSSAPISharedLibrary::release_name(
123 gssapi::OM_uint32* minor_status,
124 gssapi::gss_name_t* input_name) {
125 DCHECK(initialized_);
126 return release_name_(minor_status, input_name);
127 }
128
129 gssapi::OM_uint32 GSSAPISharedLibrary::release_buffer(
130 gssapi::OM_uint32* minor_status,
131 gssapi::gss_buffer_t buffer) {
132 DCHECK(initialized_);
133 return release_buffer_(minor_status, buffer);
134 }
135
136 gssapi::OM_uint32 GSSAPISharedLibrary::display_status(
137 gssapi::OM_uint32* minor_status,
138 gssapi::OM_uint32 status_value,
139 int status_type,
140 const gssapi::gss_OID mech_type,
141 gssapi::OM_uint32* message_context,
142 gssapi::gss_buffer_t status_string) {
143 DCHECK(initialized_);
144 return display_status_(minor_status, status_value, status_type, mech_type,
145 message_context, status_string);
146 }
147
148 gssapi::OM_uint32 GSSAPISharedLibrary::init_sec_context(
149 gssapi::OM_uint32* minor_status,
150 const gssapi::gss_cred_id_t initiator_cred_handle,
151 gssapi::gss_ctx_id_t* context_handle,
152 const gssapi::gss_name_t target_name,
153 const gssapi::gss_OID mech_type,
154 gssapi::OM_uint32 req_flags,
155 gssapi::OM_uint32 time_req,
156 const gssapi::gss_channel_bindings_t input_chan_bindings,
157 const gssapi::gss_buffer_t input_token,
158 gssapi::gss_OID* actual_mech_type,
159 gssapi::gss_buffer_t output_token,
160 gssapi::OM_uint32* ret_flags,
161 gssapi::OM_uint32* time_rec) {
162 DCHECK(initialized_);
163 return init_sec_context_(minor_status,
164 initiator_cred_handle,
165 context_handle,
166 target_name,
167 mech_type,
168 req_flags,
169 time_req,
170 input_chan_bindings,
171 input_token,
172 actual_mech_type,
173 output_token,
174 ret_flags,
175 time_rec);
176 }
177
178 gssapi::OM_uint32 GSSAPISharedLibrary::wrap_size_limit(
179 gssapi::OM_uint32* minor_status,
180 const gssapi::gss_ctx_id_t context_handle,
181 int conf_req_flag,
182 gssapi::gss_qop_t qop_req,
183 gssapi::OM_uint32 req_output_size,
184 gssapi::OM_uint32* max_input_size) {
185 DCHECK(initialized_);
186 return wrap_size_limit_(minor_status,
187 context_handle,
188 conf_req_flag,
189 qop_req,
190 req_output_size,
191 max_input_size);
192 }
193
194 GSSAPILibrary* GSSAPILibrary::GetDefault() {
195 return Singleton<GSSAPISharedLibrary>::get();
196 }
197
198 namespace {
199
200 std::string DisplayStatus(gssapi::OM_uint32 major_status,
201 gssapi::OM_uint32 minor_status) {
202 if (major_status == GSS_S_COMPLETE)
203 return "OK";
204 return StringPrintf("0x%08x 0x%08x", major_status, minor_status);
wtc 2010/06/04 21:10:34 Nit: be consistent with using either %x or %X in t
205 }
206
207 std::string DisplayCode(GSSAPILibrary* gssapi_lib,
208 gssapi::OM_uint32 status,
209 gssapi::OM_uint32 status_code_type) {
210 const int kMaxDisplayIterations = 8;
211 // msg_ctx needs to be outside the loop because it is invoked multiple times.
212 gssapi::OM_uint32 msg_ctx = 0;
213 std::string rv = StringPrintf("(0x%08X)", status);
214
215 // This loop should continue iterating until msg_ctx is 0 after the first
216 // iteration. To be cautious and prevent an infinite loop, it stops after
217 // a finite number of iterations as well.
218 for (int i = 0; i < kMaxDisplayIterations; ++i) {
219 gssapi::OM_uint32 min_stat;
220 gssapi::gss_buffer_desc_struct msg = GSS_C_EMPTY_BUFFER;
221 gssapi_lib->display_status(&min_stat, status, status_code_type,
222 GSS_C_NULL_OID,
223 &msg_ctx, &msg);
224 rv += StringPrintf(" %s", static_cast<char *>(msg.value));
225 gssapi_lib->release_buffer(&min_stat, &msg);
226 if (!msg_ctx)
227 break;
228 }
229 return rv;
230 }
231
232 std::string DisplayExtendedStatus(GSSAPILibrary* gssapi_lib,
233 gssapi::OM_uint32 major_status,
234 gssapi::OM_uint32 minor_status) {
235 if (major_status == GSS_S_COMPLETE)
236 return "OK";
237 std::string major = DisplayCode(gssapi_lib, major_status, GSS_C_GSS_CODE);
238 std::string minor = DisplayCode(gssapi_lib, minor_status, GSS_C_MECH_CODE);
239 return StringPrintf("Major: %s | Minor: %s", major.c_str(), minor.c_str());
240 }
241
242 // ScopedName releases a gssapi::gss_name_t when it goes out of scope.
243 class ScopedName {
244 public:
245 ScopedName(gssapi::gss_name_t name,
246 GSSAPILibrary* gssapi_lib)
247 : name_(name),
248 gssapi_lib_(gssapi_lib) {
249 DCHECK(gssapi_lib_);
250 }
251
252 ~ScopedName() {
253 if (name_ != GSS_C_NO_NAME) {
254 gssapi::OM_uint32 minor_status = 0;
255 gssapi::OM_uint32 major_status =
256 gssapi_lib_->release_name(&minor_status, &name_);
257 if (major_status != GSS_S_COMPLETE) {
258 LOG(WARNING) << "Problem releasing name. "
259 << DisplayStatus(major_status, minor_status);
260 }
261 name_ = GSS_C_NO_NAME;
262 }
263 }
264
265 private:
266 gssapi::gss_name_t name_;
267 GSSAPILibrary* gssapi_lib_;
268
269 DISALLOW_COPY_AND_ASSIGN(ScopedName);
270 };
271
272 // ScopedBuffer releases a gssapi::gss_buffer_t when it goes out of scope.
273 class ScopedBuffer {
wtc 2010/06/04 21:10:34 ScopedBuffer is not being used. Are you planning
274 public:
275 ScopedBuffer(gssapi::gss_buffer_t buffer,
276 GSSAPILibrary* gssapi_lib)
277 : buffer_(buffer),
278 gssapi_lib_(gssapi_lib) {
279 DCHECK(gssapi_lib_);
280 }
281
282 ~ScopedBuffer() {
283 if (buffer_ != GSS_C_NO_BUFFER) {
284 gssapi::OM_uint32 minor_status = 0;
285 gssapi::OM_uint32 major_status =
286 gssapi_lib_->release_buffer(&minor_status, buffer_);
287 if (major_status != GSS_S_COMPLETE) {
288 LOG(WARNING) << "Problem releasing buffer. "
289 << DisplayStatus(major_status, minor_status);
290 }
291 buffer_ = GSS_C_NO_BUFFER;
292 }
293 }
294
295 private:
296 gssapi::gss_buffer_t buffer_;
297 GSSAPILibrary* gssapi_lib_;
298
299 DISALLOW_COPY_AND_ASSIGN(ScopedBuffer);
300 };
301
302 } // namespace
303
304 HttpAuthGSSAPI::HttpAuthGSSAPI(GSSAPILibrary* library,
305 const std::string& scheme,
306 gssapi::gss_OID gss_oid)
307 : scheme_(scheme),
308 gss_oid_(gss_oid),
309 library_(library),
310 sec_context_(NULL) {
311 }
312
313 HttpAuthGSSAPI::~HttpAuthGSSAPI() {
314 }
315
316 bool HttpAuthGSSAPI::NeedsIdentity() const {
317 return decoded_server_auth_token_.empty();
318 }
319
320 bool HttpAuthGSSAPI::IsFinalRound() const {
321 return !NeedsIdentity();
322 }
323
324 bool HttpAuthGSSAPI::ParseChallenge(HttpAuth::ChallengeTokenizer* tok) {
325 // Verify the challenge's auth-scheme.
326 if (!tok->valid() ||
327 !LowerCaseEqualsASCII(tok->scheme(), StringToLowerASCII(scheme_).c_str()))
328 return false;
329
330 tok->set_expect_base64_token(true);
331 if (!tok->GetNext()) {
332 decoded_server_auth_token_.clear();
333 return true;
334 }
335
336 std::string encoded_auth_token = tok->value();
337 std::string decoded_auth_token;
338 bool base64_rv = base::Base64Decode(encoded_auth_token, &decoded_auth_token);
339 if (!base64_rv) {
340 LOG(ERROR) << "Base64 decoding of auth token failed.";
341 return false;
342 }
343 decoded_server_auth_token_ = decoded_auth_token;
344 return true;
345 }
346
347 int HttpAuthGSSAPI::GenerateAuthToken(const std::wstring* username,
348 const std::wstring* password,
349 const std::wstring& spn,
350 const HttpRequestInfo* request,
351 const ProxyInfo* proxy,
352 std::string* out_credentials) {
353 DCHECK(library_);
354 DCHECK((username == NULL) == (password == NULL));
355
356 library_->Init();
357
358 if (!IsFinalRound()) {
359 int rv = OnFirstRound(username, password);
360 if (rv != OK)
361 return rv;
362 }
363
364 gssapi::gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
365 input_token.length = decoded_server_auth_token_.length();
wtc 2010/06/04 21:10:34 We may need to pass GSS_NO_BUFFER instead of &inpu
366 input_token.value = const_cast<char *>(decoded_server_auth_token_.data());
367 gssapi::gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
368 int rv = GetNextSecurityToken(spn, &input_token, &output_token);
369 if (rv != OK)
370 return rv;
371
372 // Base64 encode data in output buffer and prepend the scheme.
373 std::string encode_input(static_cast<char*>(output_token.value),
374 output_token.length);
375 std::string encode_output;
376 bool ok = base::Base64Encode(encode_input, &encode_output);
377 gssapi::OM_uint32 minor_status = 0;
378 library_->release_buffer(&minor_status, &output_token);
379 if (!ok)
380 return ERR_UNEXPECTED;
381 *out_credentials = scheme_ + " " + encode_output;
382 return OK;
383 }
384
385 int HttpAuthGSSAPI::OnFirstRound(const std::wstring* username,
386 const std::wstring* password) {
387 // TODO(cbentzel): Acquire credentials?
388 DCHECK((username == NULL) == (password == NULL));
389 username_.clear();
390 password_.clear();
391 if (username) {
392 username_ = *username;
393 password_ = *password;
394 }
395 return OK;
396 }
397
398 int HttpAuthGSSAPI::GetNextSecurityToken(const std::wstring& spn,
399 gssapi::gss_buffer_t in_token,
400 gssapi::gss_buffer_t out_token) {
401 // Create a name for the principal
402 // TODO(cbentzel): Should this be username@spn? What about domain?
403 // TODO(cbentzel): Just do this on the first pass?
404 const GURL spn_url(WideToASCII(spn));
405 std::string spn_principal = GetHostAndPort(spn_url);
406 gssapi::gss_buffer_desc spn_buffer = GSS_C_EMPTY_BUFFER;
407 spn_buffer.value = const_cast<char *>(spn_principal.data());
wtc 2010/06/04 21:10:34 Use c_str() instead of data(), since spn_buffer.le
408 spn_buffer.length = spn_principal.size() + 1;
409 gssapi::OM_uint32 minor_status = 0;
410 gssapi::gss_name_t principal_name;
411 gssapi::OM_uint32 major_status = library_->import_name(
412 &minor_status,
413 &spn_buffer,
414 LOCAL_GSS_C_NT_HOSTBASED_SERVICE,
415 &principal_name);
416 if (major_status != GSS_S_COMPLETE) {
417 LOG(WARNING) << "Problem importing name. "
wtc 2010/06/04 21:10:34 This log message should be at the ERROR level if w
418 << DisplayExtendedStatus(library_,
419 major_status,
420 minor_status);
421 return ERR_UNEXPECTED;
422 }
423 ScopedName scoped_name(principal_name, library_);
424
425 // Create a security context.
wtc 2010/06/04 21:10:34 This comment may not be accurate... I think it's
426 gssapi::OM_uint32 req_flags = 0;
427 major_status = library_->init_sec_context(
428 &minor_status,
429 GSS_C_NO_CREDENTIAL,
430 &sec_context_,
wtc 2010/06/04 21:10:34 The returned sec_context_ value should be deleted
431 principal_name,
432 gss_oid_,
433 req_flags,
434 GSS_C_INDEFINITE,
435 GSS_C_NO_CHANNEL_BINDINGS,
436 in_token,
437 NULL, // actual_mech_type
438 out_token,
439 NULL, // ret flags
440 NULL);
441 if (major_status != GSS_S_COMPLETE &&
442 major_status != GSS_S_CONTINUE_NEEDED) {
443 LOG(WARNING) << "Problem initializing context. "
444 << DisplayExtendedStatus(library_,
445 major_status,
446 minor_status);
447 return ERR_UNEXPECTED;
448 }
449
450 return (major_status != GSS_S_COMPLETE) ? ERR_IO_PENDING : OK;
wtc 2010/06/04 21:10:34 Are you sure it's right to return ERR_IO_PENDING i
451 }
452
453 } // namespace net
454
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698