http_auth_gssapi_posix.cc 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879
  1. // Copyright (c) 2012 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. #include "net/http/http_auth_gssapi_posix.h"
  5. #include <limits>
  6. #include <string>
  7. #include "base/base64.h"
  8. #include "base/compiler_specific.h"
  9. #include "base/files/file_path.h"
  10. #include "base/format_macros.h"
  11. #include "base/logging.h"
  12. #include "base/memory/raw_ptr.h"
  13. #include "base/strings/string_number_conversions.h"
  14. #include "base/strings/string_piece.h"
  15. #include "base/strings/string_util.h"
  16. #include "base/strings/stringprintf.h"
  17. #include "base/threading/thread_restrictions.h"
  18. #include "base/values.h"
  19. #include "build/build_config.h"
  20. #include "net/base/net_errors.h"
  21. #include "net/http/http_auth.h"
  22. #include "net/http/http_auth_gssapi_posix.h"
  23. #include "net/http/http_auth_multi_round_parse.h"
  24. #include "net/log/net_log_event_type.h"
  25. #include "net/log/net_log_values.h"
  26. #include "net/log/net_log_with_source.h"
  27. #include "net/net_buildflags.h"
  28. namespace net {
  29. using DelegationType = HttpAuth::DelegationType;
  30. // Exported mechanism for GSSAPI. We always use SPNEGO:
  31. // iso.org.dod.internet.security.mechanism.snego (1.3.6.1.5.5.2)
  32. gss_OID_desc CHROME_GSS_SPNEGO_MECH_OID_DESC_VAL = {
  33. 6,
  34. const_cast<char*>("\x2b\x06\x01\x05\x05\x02")
  35. };
  36. gss_OID CHROME_GSS_SPNEGO_MECH_OID_DESC =
  37. &CHROME_GSS_SPNEGO_MECH_OID_DESC_VAL;
  38. OM_uint32 DelegationTypeToFlag(DelegationType delegation_type) {
  39. switch (delegation_type) {
  40. case DelegationType::kNone:
  41. return 0;
  42. case DelegationType::kByKdcPolicy:
  43. return GSS_C_DELEG_POLICY_FLAG;
  44. case DelegationType::kUnconstrained:
  45. return GSS_C_DELEG_FLAG;
  46. }
  47. }
  48. // ScopedBuffer releases a gss_buffer_t when it goes out of scope.
  49. class ScopedBuffer {
  50. public:
  51. ScopedBuffer(gss_buffer_t buffer, GSSAPILibrary* gssapi_lib)
  52. : buffer_(buffer), gssapi_lib_(gssapi_lib) {
  53. DCHECK(gssapi_lib_);
  54. }
  55. ScopedBuffer(const ScopedBuffer&) = delete;
  56. ScopedBuffer& operator=(const ScopedBuffer&) = delete;
  57. ~ScopedBuffer() {
  58. if (buffer_ != GSS_C_NO_BUFFER) {
  59. OM_uint32 minor_status = 0;
  60. OM_uint32 major_status =
  61. gssapi_lib_->release_buffer(&minor_status, buffer_);
  62. DLOG_IF(WARNING, major_status != GSS_S_COMPLETE)
  63. << "Problem releasing buffer. major=" << major_status
  64. << ", minor=" << minor_status;
  65. buffer_ = GSS_C_NO_BUFFER;
  66. }
  67. }
  68. private:
  69. gss_buffer_t buffer_;
  70. raw_ptr<GSSAPILibrary> gssapi_lib_;
  71. };
  72. // ScopedName releases a gss_name_t when it goes out of scope.
  73. class ScopedName {
  74. public:
  75. ScopedName(gss_name_t name, GSSAPILibrary* gssapi_lib)
  76. : name_(name), gssapi_lib_(gssapi_lib) {
  77. DCHECK(gssapi_lib_);
  78. }
  79. ScopedName(const ScopedName&) = delete;
  80. ScopedName& operator=(const ScopedName&) = delete;
  81. ~ScopedName() {
  82. if (name_ != GSS_C_NO_NAME) {
  83. OM_uint32 minor_status = 0;
  84. OM_uint32 major_status = gssapi_lib_->release_name(&minor_status, &name_);
  85. if (major_status != GSS_S_COMPLETE) {
  86. DLOG_IF(WARNING, major_status != GSS_S_COMPLETE)
  87. << "Problem releasing name. "
  88. << GetGssStatusValue(nullptr, "gss_release_name", major_status,
  89. minor_status);
  90. }
  91. name_ = GSS_C_NO_NAME;
  92. }
  93. }
  94. private:
  95. gss_name_t name_;
  96. raw_ptr<GSSAPILibrary> gssapi_lib_;
  97. };
  98. bool OidEquals(const gss_OID left, const gss_OID right) {
  99. if (left->length != right->length)
  100. return false;
  101. return 0 == memcmp(left->elements, right->elements, right->length);
  102. }
  103. base::Value::Dict GetGssStatusCodeValue(GSSAPILibrary* gssapi_lib,
  104. OM_uint32 status,
  105. OM_uint32 status_code_type) {
  106. base::Value::Dict rv;
  107. rv.Set("status", static_cast<int>(status));
  108. // Message lookups aren't performed if there's no library or if the status
  109. // indicates success.
  110. if (!gssapi_lib || status == GSS_S_COMPLETE)
  111. return rv;
  112. // gss_display_status() can potentially return multiple strings by sending
  113. // each string on successive invocations. State is maintained across these
  114. // invocations in a caller supplied OM_uint32. After each successful call,
  115. // the context is set to a non-zero value that should be passed as a message
  116. // context to each successive gss_display_status() call. The initial and
  117. // terminal values of this context storage is 0.
  118. OM_uint32 message_context = 0;
  119. // To account for the off chance that gss_display_status() misbehaves and gets
  120. // into an infinite loop, we'll artificially limit the number of iterations to
  121. // |kMaxDisplayIterations|. This limit is arbitrary.
  122. constexpr size_t kMaxDisplayIterations = 8;
  123. size_t iterations = 0;
  124. // In addition, each message string is again arbitrarily limited to
  125. // |kMaxMsgLength|. There's no real documented limit to work with here.
  126. constexpr size_t kMaxMsgLength = 4096;
  127. base::Value::List messages;
  128. do {
  129. gss_buffer_desc_struct message_buffer = GSS_C_EMPTY_BUFFER;
  130. ScopedBuffer message_buffer_releaser(&message_buffer, gssapi_lib);
  131. OM_uint32 minor_status = 0;
  132. OM_uint32 major_status = gssapi_lib->display_status(
  133. &minor_status, status, status_code_type, GSS_C_NO_OID, &message_context,
  134. &message_buffer);
  135. if (major_status != GSS_S_COMPLETE || message_buffer.length == 0 ||
  136. !message_buffer.value) {
  137. continue;
  138. }
  139. base::StringPiece message_string{
  140. static_cast<const char*>(message_buffer.value),
  141. std::min(kMaxMsgLength, message_buffer.length)};
  142. // The returned string is almost assuredly ASCII, but be defensive.
  143. if (!base::IsStringUTF8(message_string))
  144. continue;
  145. messages.Append(message_string);
  146. } while (message_context != 0 && ++iterations < kMaxDisplayIterations);
  147. if (!messages.empty())
  148. rv.Set("message", std::move(messages));
  149. return rv;
  150. }
  151. base::Value::Dict GetGssStatusValue(GSSAPILibrary* gssapi_lib,
  152. base::StringPiece method,
  153. OM_uint32 major_status,
  154. OM_uint32 minor_status) {
  155. base::Value::Dict params;
  156. params.Set("function", method);
  157. params.Set("major_status",
  158. GetGssStatusCodeValue(gssapi_lib, major_status, GSS_C_GSS_CODE));
  159. params.Set("minor_status",
  160. GetGssStatusCodeValue(gssapi_lib, minor_status, GSS_C_MECH_CODE));
  161. return params;
  162. }
  163. base::Value::Dict OidToValue(gss_OID oid) {
  164. base::Value::Dict params;
  165. if (!oid || oid->length == 0) {
  166. params.Set("oid", "<Empty OID>");
  167. return params;
  168. }
  169. params.Set("length", static_cast<int>(oid->length));
  170. if (!oid->elements)
  171. return params;
  172. // Cap OID content at arbitrary limit 1k.
  173. constexpr OM_uint32 kMaxOidDataSize = 1024;
  174. params.Set("bytes", NetLogBinaryValue(oid->elements, std::min(kMaxOidDataSize,
  175. oid->length)));
  176. // Based on RFC 2744 Appendix A. Hardcoding the OIDs in the list below to
  177. // avoid having a static dependency on the library.
  178. static const struct {
  179. const char* symbolic_name;
  180. const gss_OID_desc oid_desc;
  181. } kWellKnownOIDs[] = {
  182. {"GSS_C_NT_USER_NAME",
  183. {10, const_cast<char*>("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x01")}},
  184. {"GSS_C_NT_MACHINE_UID_NAME",
  185. {10, const_cast<char*>("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x02")}},
  186. {"GSS_C_NT_STRING_UID_NAME",
  187. {10, const_cast<char*>("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x03")}},
  188. {"GSS_C_NT_HOSTBASED_SERVICE_X",
  189. {6, const_cast<char*>("\x2b\x06\x01\x05\x06\x02")}},
  190. {"GSS_C_NT_HOSTBASED_SERVICE",
  191. {10, const_cast<char*>("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04")}},
  192. {"GSS_C_NT_ANONYMOUS", {6, const_cast<char*>("\x2b\x06\01\x05\x06\x03")}},
  193. {"GSS_C_NT_EXPORT_NAME",
  194. {6, const_cast<char*>("\x2b\x06\x01\x05\x06\x04")}}};
  195. for (auto& well_known_oid : kWellKnownOIDs) {
  196. if (OidEquals(oid, const_cast<const gss_OID>(&well_known_oid.oid_desc)))
  197. params.Set("oid", well_known_oid.symbolic_name);
  198. }
  199. return params;
  200. }
  201. base::Value::Dict GetDisplayNameValue(GSSAPILibrary* gssapi_lib,
  202. const gss_name_t gss_name) {
  203. OM_uint32 major_status = 0;
  204. OM_uint32 minor_status = 0;
  205. gss_buffer_desc_struct name = GSS_C_EMPTY_BUFFER;
  206. gss_OID name_type = GSS_C_NO_OID;
  207. base::Value::Dict rv;
  208. major_status =
  209. gssapi_lib->display_name(&minor_status, gss_name, &name, &name_type);
  210. ScopedBuffer scoped_output_name(&name, gssapi_lib);
  211. if (major_status != GSS_S_COMPLETE) {
  212. rv.Set("error", GetGssStatusValue(gssapi_lib, "gss_display_name",
  213. major_status, minor_status));
  214. return rv;
  215. }
  216. auto name_string =
  217. base::StringPiece(reinterpret_cast<const char*>(name.value), name.length);
  218. rv.Set("name", base::IsStringUTF8(name_string)
  219. ? NetLogStringValue(name_string)
  220. : NetLogBinaryValue(name.value, name.length));
  221. rv.Set("type", OidToValue(name_type));
  222. return rv;
  223. }
  224. base::Value::Dict ContextFlagsToValue(OM_uint32 flags) {
  225. base::Value::Dict rv;
  226. rv.Set("value", base::StringPrintf("0x%08x", flags));
  227. rv.Set("delegated", (flags & GSS_C_DELEG_FLAG) == GSS_C_DELEG_FLAG);
  228. rv.Set("mutual", (flags & GSS_C_MUTUAL_FLAG) == GSS_C_MUTUAL_FLAG);
  229. return rv;
  230. }
  231. base::Value GetContextStateAsValue(GSSAPILibrary* gssapi_lib,
  232. const gss_ctx_id_t context_handle) {
  233. base::Value::Dict rv;
  234. if (context_handle == GSS_C_NO_CONTEXT) {
  235. rv.Set("error", GetGssStatusValue(nullptr, "<none>", GSS_S_NO_CONTEXT, 0));
  236. return base::Value(std::move(rv));
  237. }
  238. OM_uint32 major_status = 0;
  239. OM_uint32 minor_status = 0;
  240. gss_name_t src_name = GSS_C_NO_NAME;
  241. gss_name_t targ_name = GSS_C_NO_NAME;
  242. OM_uint32 lifetime_rec = 0;
  243. gss_OID mech_type = GSS_C_NO_OID;
  244. OM_uint32 ctx_flags = 0;
  245. int locally_initiated = 0;
  246. int open = 0;
  247. major_status = gssapi_lib->inquire_context(&minor_status,
  248. context_handle,
  249. &src_name,
  250. &targ_name,
  251. &lifetime_rec,
  252. &mech_type,
  253. &ctx_flags,
  254. &locally_initiated,
  255. &open);
  256. if (major_status != GSS_S_COMPLETE) {
  257. rv.Set("error", GetGssStatusValue(gssapi_lib, "gss_inquire_context",
  258. major_status, minor_status));
  259. return base::Value(std::move(rv));
  260. }
  261. ScopedName scoped_src_name(src_name, gssapi_lib);
  262. ScopedName scoped_targ_name(targ_name, gssapi_lib);
  263. rv.Set("source", GetDisplayNameValue(gssapi_lib, src_name));
  264. rv.Set("target", GetDisplayNameValue(gssapi_lib, targ_name));
  265. // lifetime_rec is a uint32, while base::Value only takes ints. On 32 bit
  266. // platforms uint32 doesn't fit on an int.
  267. rv.Set("lifetime", base::NumberToString(lifetime_rec));
  268. rv.Set("mechanism", OidToValue(mech_type));
  269. rv.Set("flags", ContextFlagsToValue(ctx_flags));
  270. rv.Set("open", !!open);
  271. return base::Value(std::move(rv));
  272. }
  273. namespace {
  274. // Return a NetLog value for the result of loading a library.
  275. base::Value LibraryLoadResultParams(base::StringPiece library_name,
  276. base::StringPiece load_result) {
  277. base::Value::Dict params;
  278. params.Set("library_name", library_name);
  279. if (!load_result.empty())
  280. params.Set("load_result", load_result);
  281. return base::Value(std::move(params));
  282. }
  283. } // namespace
  284. GSSAPISharedLibrary::GSSAPISharedLibrary(const std::string& gssapi_library_name)
  285. : gssapi_library_name_(gssapi_library_name) {}
  286. GSSAPISharedLibrary::~GSSAPISharedLibrary() {
  287. if (gssapi_library_) {
  288. base::UnloadNativeLibrary(gssapi_library_);
  289. gssapi_library_ = nullptr;
  290. }
  291. }
  292. bool GSSAPISharedLibrary::Init(const NetLogWithSource& net_log) {
  293. if (!initialized_)
  294. InitImpl(net_log);
  295. return initialized_;
  296. }
  297. bool GSSAPISharedLibrary::InitImpl(const NetLogWithSource& net_log) {
  298. DCHECK(!initialized_);
  299. gssapi_library_ = LoadSharedLibrary(net_log);
  300. if (gssapi_library_ == nullptr)
  301. return false;
  302. initialized_ = true;
  303. return true;
  304. }
  305. base::NativeLibrary GSSAPISharedLibrary::LoadSharedLibrary(
  306. const NetLogWithSource& net_log) {
  307. const char* const* library_names;
  308. size_t num_lib_names;
  309. const char* user_specified_library[1];
  310. if (!gssapi_library_name_.empty()) {
  311. user_specified_library[0] = gssapi_library_name_.c_str();
  312. library_names = user_specified_library;
  313. num_lib_names = 1;
  314. } else {
  315. static const char* const kDefaultLibraryNames[] = {
  316. #if BUILDFLAG(IS_APPLE)
  317. "/System/Library/Frameworks/GSS.framework/GSS"
  318. #elif BUILDFLAG(IS_OPENBSD)
  319. "libgssapi.so" // Heimdal - OpenBSD
  320. #else
  321. "libgssapi_krb5.so.2", // MIT Kerberos - FC, Suse10, Debian
  322. "libgssapi.so.4", // Heimdal - Suse10, MDK
  323. "libgssapi.so.2", // Heimdal - Gentoo
  324. "libgssapi.so.1" // Heimdal - Suse9, CITI - FC, MDK, Suse10
  325. #endif
  326. };
  327. library_names = kDefaultLibraryNames;
  328. num_lib_names = std::size(kDefaultLibraryNames);
  329. }
  330. net_log.BeginEvent(NetLogEventType::AUTH_LIBRARY_LOAD);
  331. // There has to be at least one candidate.
  332. DCHECK_NE(0u, num_lib_names);
  333. const char* library_name = nullptr;
  334. base::NativeLibraryLoadError load_error;
  335. for (size_t i = 0; i < num_lib_names; ++i) {
  336. load_error = base::NativeLibraryLoadError();
  337. library_name = library_names[i];
  338. base::FilePath file_path(library_name);
  339. // TODO(asanka): Move library loading to a separate thread.
  340. // http://crbug.com/66702
  341. base::ThreadRestrictions::ScopedAllowIO allow_io_temporarily;
  342. base::NativeLibrary lib = base::LoadNativeLibrary(file_path, &load_error);
  343. if (lib) {
  344. if (BindMethods(lib, library_name, net_log)) {
  345. net_log.EndEvent(NetLogEventType::AUTH_LIBRARY_LOAD, [&] {
  346. return LibraryLoadResultParams(library_name, "");
  347. });
  348. return lib;
  349. }
  350. base::UnloadNativeLibrary(lib);
  351. }
  352. }
  353. // If loading failed, then log the result of the final attempt. Doing so
  354. // is specially important on platforms where there's only one possible
  355. // library. Doing so also always logs the failure when the GSSAPI library
  356. // name is explicitly specified.
  357. net_log.EndEvent(NetLogEventType::AUTH_LIBRARY_LOAD, [&] {
  358. return LibraryLoadResultParams(library_name, load_error.ToString());
  359. });
  360. return nullptr;
  361. }
  362. namespace {
  363. base::Value BindFailureParams(base::StringPiece library_name,
  364. base::StringPiece method) {
  365. base::Value::Dict params;
  366. params.Set("library_name", library_name);
  367. params.Set("method", method);
  368. return base::Value(std::move(params));
  369. }
  370. void* BindUntypedMethod(base::NativeLibrary lib,
  371. base::StringPiece library_name,
  372. base::StringPiece method,
  373. const NetLogWithSource& net_log) {
  374. void* ptr = base::GetFunctionPointerFromNativeLibrary(lib, method);
  375. if (ptr == nullptr) {
  376. net_log.AddEvent(NetLogEventType::AUTH_LIBRARY_BIND_FAILED,
  377. [&] { return BindFailureParams(library_name, method); });
  378. }
  379. return ptr;
  380. }
  381. template <typename T>
  382. bool BindMethod(base::NativeLibrary lib,
  383. base::StringPiece library_name,
  384. base::StringPiece method,
  385. T* receiver,
  386. const NetLogWithSource& net_log) {
  387. *receiver = reinterpret_cast<T>(
  388. BindUntypedMethod(lib, library_name, method, net_log));
  389. return *receiver != nullptr;
  390. }
  391. } // namespace
  392. bool GSSAPISharedLibrary::BindMethods(base::NativeLibrary lib,
  393. base::StringPiece name,
  394. const NetLogWithSource& net_log) {
  395. bool ok = true;
  396. // It's unlikely for BindMethods() to fail if LoadNativeLibrary() succeeded. A
  397. // failure in this function indicates an interoperability issue whose
  398. // diagnosis requires knowing all the methods that are missing. Hence |ok| is
  399. // updated in a manner that prevents short-circuiting the BindGssMethod()
  400. // invocations.
  401. ok &= BindMethod(lib, name, "gss_delete_sec_context", &delete_sec_context_,
  402. net_log);
  403. ok &= BindMethod(lib, name, "gss_display_name", &display_name_, net_log);
  404. ok &= BindMethod(lib, name, "gss_display_status", &display_status_, net_log);
  405. ok &= BindMethod(lib, name, "gss_import_name", &import_name_, net_log);
  406. ok &= BindMethod(lib, name, "gss_init_sec_context", &init_sec_context_,
  407. net_log);
  408. ok &=
  409. BindMethod(lib, name, "gss_inquire_context", &inquire_context_, net_log);
  410. ok &= BindMethod(lib, name, "gss_release_buffer", &release_buffer_, net_log);
  411. ok &= BindMethod(lib, name, "gss_release_name", &release_name_, net_log);
  412. ok &=
  413. BindMethod(lib, name, "gss_wrap_size_limit", &wrap_size_limit_, net_log);
  414. if (LIKELY(ok))
  415. return true;
  416. delete_sec_context_ = nullptr;
  417. display_name_ = nullptr;
  418. display_status_ = nullptr;
  419. import_name_ = nullptr;
  420. init_sec_context_ = nullptr;
  421. inquire_context_ = nullptr;
  422. release_buffer_ = nullptr;
  423. release_name_ = nullptr;
  424. wrap_size_limit_ = nullptr;
  425. return false;
  426. }
  427. OM_uint32 GSSAPISharedLibrary::import_name(
  428. OM_uint32* minor_status,
  429. const gss_buffer_t input_name_buffer,
  430. const gss_OID input_name_type,
  431. gss_name_t* output_name) {
  432. DCHECK(initialized_);
  433. return import_name_(minor_status, input_name_buffer, input_name_type,
  434. output_name);
  435. }
  436. OM_uint32 GSSAPISharedLibrary::release_name(
  437. OM_uint32* minor_status,
  438. gss_name_t* input_name) {
  439. DCHECK(initialized_);
  440. return release_name_(minor_status, input_name);
  441. }
  442. OM_uint32 GSSAPISharedLibrary::release_buffer(
  443. OM_uint32* minor_status,
  444. gss_buffer_t buffer) {
  445. DCHECK(initialized_);
  446. return release_buffer_(minor_status, buffer);
  447. }
  448. OM_uint32 GSSAPISharedLibrary::display_name(
  449. OM_uint32* minor_status,
  450. const gss_name_t input_name,
  451. gss_buffer_t output_name_buffer,
  452. gss_OID* output_name_type) {
  453. DCHECK(initialized_);
  454. return display_name_(minor_status,
  455. input_name,
  456. output_name_buffer,
  457. output_name_type);
  458. }
  459. OM_uint32 GSSAPISharedLibrary::display_status(
  460. OM_uint32* minor_status,
  461. OM_uint32 status_value,
  462. int status_type,
  463. const gss_OID mech_type,
  464. OM_uint32* message_context,
  465. gss_buffer_t status_string) {
  466. DCHECK(initialized_);
  467. return display_status_(minor_status, status_value, status_type, mech_type,
  468. message_context, status_string);
  469. }
  470. OM_uint32 GSSAPISharedLibrary::init_sec_context(
  471. OM_uint32* minor_status,
  472. const gss_cred_id_t initiator_cred_handle,
  473. gss_ctx_id_t* context_handle,
  474. const gss_name_t target_name,
  475. const gss_OID mech_type,
  476. OM_uint32 req_flags,
  477. OM_uint32 time_req,
  478. const gss_channel_bindings_t input_chan_bindings,
  479. const gss_buffer_t input_token,
  480. gss_OID* actual_mech_type,
  481. gss_buffer_t output_token,
  482. OM_uint32* ret_flags,
  483. OM_uint32* time_rec) {
  484. DCHECK(initialized_);
  485. return init_sec_context_(minor_status,
  486. initiator_cred_handle,
  487. context_handle,
  488. target_name,
  489. mech_type,
  490. req_flags,
  491. time_req,
  492. input_chan_bindings,
  493. input_token,
  494. actual_mech_type,
  495. output_token,
  496. ret_flags,
  497. time_rec);
  498. }
  499. OM_uint32 GSSAPISharedLibrary::wrap_size_limit(
  500. OM_uint32* minor_status,
  501. const gss_ctx_id_t context_handle,
  502. int conf_req_flag,
  503. gss_qop_t qop_req,
  504. OM_uint32 req_output_size,
  505. OM_uint32* max_input_size) {
  506. DCHECK(initialized_);
  507. return wrap_size_limit_(minor_status,
  508. context_handle,
  509. conf_req_flag,
  510. qop_req,
  511. req_output_size,
  512. max_input_size);
  513. }
  514. OM_uint32 GSSAPISharedLibrary::delete_sec_context(
  515. OM_uint32* minor_status,
  516. gss_ctx_id_t* context_handle,
  517. gss_buffer_t output_token) {
  518. // This is called from the owner class' destructor, even if
  519. // Init() is not called, so we can't assume |initialized_|
  520. // is set.
  521. if (!initialized_)
  522. return 0;
  523. return delete_sec_context_(minor_status,
  524. context_handle,
  525. output_token);
  526. }
  527. OM_uint32 GSSAPISharedLibrary::inquire_context(
  528. OM_uint32* minor_status,
  529. const gss_ctx_id_t context_handle,
  530. gss_name_t* src_name,
  531. gss_name_t* targ_name,
  532. OM_uint32* lifetime_rec,
  533. gss_OID* mech_type,
  534. OM_uint32* ctx_flags,
  535. int* locally_initiated,
  536. int* open) {
  537. DCHECK(initialized_);
  538. return inquire_context_(minor_status,
  539. context_handle,
  540. src_name,
  541. targ_name,
  542. lifetime_rec,
  543. mech_type,
  544. ctx_flags,
  545. locally_initiated,
  546. open);
  547. }
  548. const std::string& GSSAPISharedLibrary::GetLibraryNameForTesting() {
  549. return gssapi_library_name_;
  550. }
  551. ScopedSecurityContext::ScopedSecurityContext(GSSAPILibrary* gssapi_lib)
  552. : security_context_(GSS_C_NO_CONTEXT),
  553. gssapi_lib_(gssapi_lib) {
  554. DCHECK(gssapi_lib_);
  555. }
  556. ScopedSecurityContext::~ScopedSecurityContext() {
  557. if (security_context_ != GSS_C_NO_CONTEXT) {
  558. gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
  559. OM_uint32 minor_status = 0;
  560. OM_uint32 major_status = gssapi_lib_->delete_sec_context(
  561. &minor_status, &security_context_, &output_token);
  562. DLOG_IF(WARNING, major_status != GSS_S_COMPLETE)
  563. << "Problem releasing security_context. "
  564. << GetGssStatusValue(gssapi_lib_, "delete_sec_context", major_status,
  565. minor_status);
  566. security_context_ = GSS_C_NO_CONTEXT;
  567. }
  568. }
  569. HttpAuthGSSAPI::HttpAuthGSSAPI(GSSAPILibrary* library, gss_OID gss_oid)
  570. : gss_oid_(gss_oid), library_(library), scoped_sec_context_(library) {
  571. DCHECK(library_);
  572. }
  573. HttpAuthGSSAPI::~HttpAuthGSSAPI() = default;
  574. bool HttpAuthGSSAPI::Init(const NetLogWithSource& net_log) {
  575. if (!library_)
  576. return false;
  577. return library_->Init(net_log);
  578. }
  579. bool HttpAuthGSSAPI::NeedsIdentity() const {
  580. return decoded_server_auth_token_.empty();
  581. }
  582. bool HttpAuthGSSAPI::AllowsExplicitCredentials() const {
  583. return false;
  584. }
  585. void HttpAuthGSSAPI::SetDelegation(DelegationType delegation_type) {
  586. delegation_type_ = delegation_type;
  587. }
  588. HttpAuth::AuthorizationResult HttpAuthGSSAPI::ParseChallenge(
  589. HttpAuthChallengeTokenizer* tok) {
  590. if (scoped_sec_context_.get() == GSS_C_NO_CONTEXT) {
  591. return net::ParseFirstRoundChallenge(HttpAuth::AUTH_SCHEME_NEGOTIATE, tok);
  592. }
  593. std::string encoded_auth_token;
  594. return net::ParseLaterRoundChallenge(HttpAuth::AUTH_SCHEME_NEGOTIATE, tok,
  595. &encoded_auth_token,
  596. &decoded_server_auth_token_);
  597. }
  598. int HttpAuthGSSAPI::GenerateAuthToken(const AuthCredentials* credentials,
  599. const std::string& spn,
  600. const std::string& channel_bindings,
  601. std::string* auth_token,
  602. const NetLogWithSource& net_log,
  603. CompletionOnceCallback /*callback*/) {
  604. DCHECK(auth_token);
  605. gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
  606. input_token.length = decoded_server_auth_token_.length();
  607. input_token.value = (input_token.length > 0)
  608. ? const_cast<char*>(decoded_server_auth_token_.data())
  609. : nullptr;
  610. gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
  611. ScopedBuffer scoped_output_token(&output_token, library_);
  612. int rv = GetNextSecurityToken(spn, channel_bindings, &input_token,
  613. &output_token, net_log);
  614. if (rv != OK)
  615. return rv;
  616. // Base64 encode data in output buffer and prepend the scheme.
  617. std::string encode_input(static_cast<char*>(output_token.value),
  618. output_token.length);
  619. std::string encode_output;
  620. base::Base64Encode(encode_input, &encode_output);
  621. *auth_token = "Negotiate " + encode_output;
  622. return OK;
  623. }
  624. namespace {
  625. // GSSAPI status codes consist of a calling error (essentially, a programmer
  626. // bug), a routine error (defined by the RFC), and supplementary information,
  627. // all bitwise-or'ed together in different regions of the 32 bit return value.
  628. // This means a simple switch on the return codes is not sufficient.
  629. int MapImportNameStatusToError(OM_uint32 major_status) {
  630. if (major_status == GSS_S_COMPLETE)
  631. return OK;
  632. if (GSS_CALLING_ERROR(major_status) != 0)
  633. return ERR_UNEXPECTED;
  634. OM_uint32 routine_error = GSS_ROUTINE_ERROR(major_status);
  635. switch (routine_error) {
  636. case GSS_S_FAILURE:
  637. // Looking at the MIT Kerberos implementation, this typically is returned
  638. // when memory allocation fails. However, the API does not guarantee
  639. // that this is the case, so using ERR_UNEXPECTED rather than
  640. // ERR_OUT_OF_MEMORY.
  641. return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS;
  642. case GSS_S_BAD_NAME:
  643. case GSS_S_BAD_NAMETYPE:
  644. return ERR_MALFORMED_IDENTITY;
  645. case GSS_S_DEFECTIVE_TOKEN:
  646. // Not mentioned in the API, but part of code.
  647. return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS;
  648. case GSS_S_BAD_MECH:
  649. return ERR_UNSUPPORTED_AUTH_SCHEME;
  650. default:
  651. return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS;
  652. }
  653. }
  654. int MapInitSecContextStatusToError(OM_uint32 major_status) {
  655. // Although GSS_S_CONTINUE_NEEDED is an additional bit, it seems like
  656. // other code just checks if major_status is equivalent to it to indicate
  657. // that there are no other errors included.
  658. if (major_status == GSS_S_COMPLETE || major_status == GSS_S_CONTINUE_NEEDED)
  659. return OK;
  660. if (GSS_CALLING_ERROR(major_status) != 0)
  661. return ERR_UNEXPECTED;
  662. OM_uint32 routine_status = GSS_ROUTINE_ERROR(major_status);
  663. switch (routine_status) {
  664. case GSS_S_DEFECTIVE_TOKEN:
  665. return ERR_INVALID_RESPONSE;
  666. case GSS_S_DEFECTIVE_CREDENTIAL:
  667. // Not expected since this implementation uses the default credential.
  668. return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS;
  669. case GSS_S_BAD_SIG:
  670. // Probably won't happen, but it's a bad response.
  671. return ERR_INVALID_RESPONSE;
  672. case GSS_S_NO_CRED:
  673. return ERR_INVALID_AUTH_CREDENTIALS;
  674. case GSS_S_CREDENTIALS_EXPIRED:
  675. return ERR_INVALID_AUTH_CREDENTIALS;
  676. case GSS_S_BAD_BINDINGS:
  677. // This only happens with mutual authentication.
  678. return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS;
  679. case GSS_S_NO_CONTEXT:
  680. return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS;
  681. case GSS_S_BAD_NAMETYPE:
  682. return ERR_UNSUPPORTED_AUTH_SCHEME;
  683. case GSS_S_BAD_NAME:
  684. return ERR_UNSUPPORTED_AUTH_SCHEME;
  685. case GSS_S_BAD_MECH:
  686. return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS;
  687. case GSS_S_FAILURE:
  688. // This should be an "Unexpected Security Status" according to the
  689. // GSSAPI documentation, but it's typically used to indicate that
  690. // credentials are not correctly set up on a user machine, such
  691. // as a missing credential cache or hitting this after calling
  692. // kdestroy.
  693. // TODO(cbentzel): Use minor code for even better mapping?
  694. return ERR_MISSING_AUTH_CREDENTIALS;
  695. default:
  696. if (routine_status != 0)
  697. return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS;
  698. break;
  699. }
  700. OM_uint32 supplemental_status = GSS_SUPPLEMENTARY_INFO(major_status);
  701. // Replays could indicate an attack.
  702. if (supplemental_status & (GSS_S_DUPLICATE_TOKEN | GSS_S_OLD_TOKEN |
  703. GSS_S_UNSEQ_TOKEN | GSS_S_GAP_TOKEN))
  704. return ERR_INVALID_RESPONSE;
  705. // At this point, every documented status has been checked.
  706. return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS;
  707. }
  708. base::Value ImportNameErrorParams(GSSAPILibrary* library,
  709. base::StringPiece spn,
  710. OM_uint32 major_status,
  711. OM_uint32 minor_status) {
  712. base::Value::Dict params;
  713. params.Set("spn", spn);
  714. if (major_status != GSS_S_COMPLETE)
  715. params.Set("status", GetGssStatusValue(library, "import_name", major_status,
  716. minor_status));
  717. return base::Value(std::move(params));
  718. }
  719. base::Value InitSecContextErrorParams(GSSAPILibrary* library,
  720. gss_ctx_id_t context,
  721. OM_uint32 major_status,
  722. OM_uint32 minor_status) {
  723. base::Value::Dict params;
  724. if (major_status != GSS_S_COMPLETE)
  725. params.Set("status", GetGssStatusValue(library, "gss_init_sec_context",
  726. major_status, minor_status));
  727. if (context != GSS_C_NO_CONTEXT)
  728. params.Set("context", GetContextStateAsValue(library, context));
  729. return base::Value(std::move(params));
  730. }
  731. } // anonymous namespace
  732. int HttpAuthGSSAPI::GetNextSecurityToken(const std::string& spn,
  733. const std::string& channel_bindings,
  734. gss_buffer_t in_token,
  735. gss_buffer_t out_token,
  736. const NetLogWithSource& net_log) {
  737. // GSSAPI header files, to this day, require OIDs passed in as non-const
  738. // pointers. Rather than const casting, let's just leave this as non-const.
  739. // Even if the OID pointer is const, the inner |elements| pointer is still
  740. // non-const.
  741. static gss_OID_desc kGSS_C_NT_HOSTBASED_SERVICE = {
  742. 10, const_cast<char*>("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04")};
  743. // Create a name for the principal
  744. // TODO(cbentzel): Just do this on the first pass?
  745. std::string spn_principal = spn;
  746. gss_buffer_desc spn_buffer = GSS_C_EMPTY_BUFFER;
  747. spn_buffer.value = const_cast<char*>(spn_principal.c_str());
  748. spn_buffer.length = spn_principal.size() + 1;
  749. OM_uint32 minor_status = 0;
  750. gss_name_t principal_name = GSS_C_NO_NAME;
  751. OM_uint32 major_status =
  752. library_->import_name(&minor_status, &spn_buffer,
  753. &kGSS_C_NT_HOSTBASED_SERVICE, &principal_name);
  754. net_log.AddEvent(NetLogEventType::AUTH_LIBRARY_IMPORT_NAME, [&] {
  755. return ImportNameErrorParams(library_, spn, major_status, minor_status);
  756. });
  757. int rv = MapImportNameStatusToError(major_status);
  758. if (rv != OK)
  759. return rv;
  760. ScopedName scoped_name(principal_name, library_);
  761. // Continue creating a security context.
  762. net_log.BeginEvent(NetLogEventType::AUTH_LIBRARY_INIT_SEC_CTX);
  763. major_status = library_->init_sec_context(
  764. &minor_status, GSS_C_NO_CREDENTIAL, scoped_sec_context_.receive(),
  765. principal_name, gss_oid_, DelegationTypeToFlag(delegation_type_),
  766. GSS_C_INDEFINITE, GSS_C_NO_CHANNEL_BINDINGS, in_token,
  767. nullptr, // actual_mech_type
  768. out_token,
  769. nullptr, // ret flags
  770. nullptr);
  771. net_log.EndEvent(NetLogEventType::AUTH_LIBRARY_INIT_SEC_CTX, [&] {
  772. return InitSecContextErrorParams(library_, scoped_sec_context_.get(),
  773. major_status, minor_status);
  774. });
  775. return MapInitSecContextStatusToError(major_status);
  776. }
  777. } // namespace net