nsAuthGSSAPI.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this
  3. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  4. //
  5. // GSSAPI Authentication Support Module
  6. //
  7. // Described by IETF Internet draft: draft-brezak-kerberos-http-00.txt
  8. // (formerly draft-brezak-spnego-http-04.txt)
  9. //
  10. // Also described here:
  11. // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnsecure/html/http-sso-1.asp
  12. //
  13. //
  14. #include "mozilla/ArrayUtils.h"
  15. #include "prlink.h"
  16. #include "nsCOMPtr.h"
  17. #include "nsIPrefService.h"
  18. #include "nsIPrefBranch.h"
  19. #include "nsIServiceManager.h"
  20. #include "nsNativeCharsetUtils.h"
  21. #include "nsAuthGSSAPI.h"
  22. #if defined(HAVE_RES_NINIT)
  23. #include <sys/types.h>
  24. #include <netinet/in.h>
  25. #include <arpa/nameser.h>
  26. #include <resolv.h>
  27. #endif
  28. using namespace mozilla;
  29. //-----------------------------------------------------------------------------
  30. // We define GSS_C_NT_HOSTBASED_SERVICE explicitly since it may be referenced
  31. // by by a different name depending on the implementation of gss but always
  32. // has the same value
  33. static gss_OID_desc gss_c_nt_hostbased_service =
  34. { 10, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04" };
  35. static const char kNegotiateAuthGssLib[] =
  36. "network.negotiate-auth.gsslib";
  37. static const char kNegotiateAuthNativeImp[] =
  38. "network.negotiate-auth.using-native-gsslib";
  39. static struct GSSFunction {
  40. const char *str;
  41. PRFuncPtr func;
  42. } gssFuncs[] = {
  43. { "gss_display_status", nullptr },
  44. { "gss_init_sec_context", nullptr },
  45. { "gss_indicate_mechs", nullptr },
  46. { "gss_release_oid_set", nullptr },
  47. { "gss_delete_sec_context", nullptr },
  48. { "gss_import_name", nullptr },
  49. { "gss_release_buffer", nullptr },
  50. { "gss_release_name", nullptr },
  51. { "gss_wrap", nullptr },
  52. { "gss_unwrap", nullptr }
  53. };
  54. static bool gssNativeImp = true;
  55. static PRLibrary* gssLibrary = nullptr;
  56. #define gss_display_status_ptr ((gss_display_status_type)*gssFuncs[0].func)
  57. #define gss_init_sec_context_ptr ((gss_init_sec_context_type)*gssFuncs[1].func)
  58. #define gss_indicate_mechs_ptr ((gss_indicate_mechs_type)*gssFuncs[2].func)
  59. #define gss_release_oid_set_ptr ((gss_release_oid_set_type)*gssFuncs[3].func)
  60. #define gss_delete_sec_context_ptr ((gss_delete_sec_context_type)*gssFuncs[4].func)
  61. #define gss_import_name_ptr ((gss_import_name_type)*gssFuncs[5].func)
  62. #define gss_release_buffer_ptr ((gss_release_buffer_type)*gssFuncs[6].func)
  63. #define gss_release_name_ptr ((gss_release_name_type)*gssFuncs[7].func)
  64. #define gss_wrap_ptr ((gss_wrap_type)*gssFuncs[8].func)
  65. #define gss_unwrap_ptr ((gss_unwrap_type)*gssFuncs[9].func)
  66. static nsresult
  67. gssInit()
  68. {
  69. nsXPIDLCString libPath;
  70. nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
  71. if (prefs) {
  72. prefs->GetCharPref(kNegotiateAuthGssLib, getter_Copies(libPath));
  73. prefs->GetBoolPref(kNegotiateAuthNativeImp, &gssNativeImp);
  74. }
  75. PRLibrary *lib = nullptr;
  76. if (!libPath.IsEmpty()) {
  77. LOG(("Attempting to load user specified library [%s]\n", libPath.get()));
  78. gssNativeImp = false;
  79. lib = PR_LoadLibrary(libPath.get());
  80. }
  81. else {
  82. #ifdef XP_WIN
  83. char *libName = PR_GetLibraryName(nullptr, "gssapi32");
  84. if (libName) {
  85. lib = PR_LoadLibrary("gssapi32");
  86. PR_FreeLibraryName(libName);
  87. }
  88. #elif defined(__OpenBSD__)
  89. /* OpenBSD doesn't register inter-library dependencies in basesystem
  90. * libs therefor we need to load all the libraries gssapi depends on,
  91. * in the correct order and with LD_GLOBAL for GSSAPI auth to work
  92. * fine.
  93. */
  94. const char *const verLibNames[] = {
  95. "libasn1.so",
  96. "libcrypto.so",
  97. "libroken.so",
  98. "libheimbase.so",
  99. "libcom_err.so",
  100. "libkrb5.so",
  101. "libgssapi.so"
  102. };
  103. PRLibSpec libSpec;
  104. for (size_t i = 0; i < ArrayLength(verLibNames); ++i) {
  105. libSpec.type = PR_LibSpec_Pathname;
  106. libSpec.value.pathname = verLibNames[i];
  107. lib = PR_LoadLibraryWithFlags(libSpec, PR_LD_GLOBAL);
  108. }
  109. #else
  110. const char *const libNames[] = {
  111. "gss",
  112. "gssapi_krb5",
  113. "gssapi"
  114. };
  115. const char *const verLibNames[] = {
  116. "libgssapi_krb5.so.2", /* MIT - FC, Suse10, Debian */
  117. "libgssapi.so.4", /* Heimdal - Suse10, MDK */
  118. "libgssapi.so.1" /* Heimdal - Suse9, CITI - FC, MDK, Suse10*/
  119. };
  120. for (size_t i = 0; i < ArrayLength(verLibNames) && !lib; ++i) {
  121. lib = PR_LoadLibrary(verLibNames[i]);
  122. /* The CITI libgssapi library calls exit() during
  123. * initialization if it's not correctly configured. Try to
  124. * ensure that we never use this library for our GSSAPI
  125. * support, as its just a wrapper library, anyway.
  126. * See Bugzilla #325433
  127. */
  128. if (lib &&
  129. PR_FindFunctionSymbol(lib,
  130. "internal_krb5_gss_initialize") &&
  131. PR_FindFunctionSymbol(lib, "gssd_pname_to_uid")) {
  132. LOG(("CITI libgssapi found, which calls exit(). Skipping\n"));
  133. PR_UnloadLibrary(lib);
  134. lib = nullptr;
  135. }
  136. }
  137. for (size_t i = 0; i < ArrayLength(libNames) && !lib; ++i) {
  138. char *libName = PR_GetLibraryName(nullptr, libNames[i]);
  139. if (libName) {
  140. lib = PR_LoadLibrary(libName);
  141. PR_FreeLibraryName(libName);
  142. if (lib &&
  143. PR_FindFunctionSymbol(lib,
  144. "internal_krb5_gss_initialize") &&
  145. PR_FindFunctionSymbol(lib, "gssd_pname_to_uid")) {
  146. LOG(("CITI libgssapi found, which calls exit(). Skipping\n"));
  147. PR_UnloadLibrary(lib);
  148. lib = nullptr;
  149. }
  150. }
  151. }
  152. #endif
  153. }
  154. if (!lib) {
  155. LOG(("Fail to load gssapi library\n"));
  156. return NS_ERROR_FAILURE;
  157. }
  158. LOG(("Attempting to load gss functions\n"));
  159. for (size_t i = 0; i < ArrayLength(gssFuncs); ++i) {
  160. gssFuncs[i].func = PR_FindFunctionSymbol(lib, gssFuncs[i].str);
  161. if (!gssFuncs[i].func) {
  162. LOG(("Fail to load %s function from gssapi library\n", gssFuncs[i].str));
  163. PR_UnloadLibrary(lib);
  164. return NS_ERROR_FAILURE;
  165. }
  166. }
  167. gssLibrary = lib;
  168. return NS_OK;
  169. }
  170. // Generate proper GSSAPI error messages from the major and
  171. // minor status codes.
  172. void
  173. LogGssError(OM_uint32 maj_stat, OM_uint32 min_stat, const char *prefix)
  174. {
  175. if (!MOZ_LOG_TEST(gNegotiateLog, LogLevel::Debug)) {
  176. return;
  177. }
  178. OM_uint32 new_stat;
  179. OM_uint32 msg_ctx = 0;
  180. gss_buffer_desc status1_string;
  181. gss_buffer_desc status2_string;
  182. OM_uint32 ret;
  183. nsAutoCString errorStr;
  184. errorStr.Assign(prefix);
  185. if (!gssLibrary)
  186. return;
  187. errorStr += ": ";
  188. do {
  189. ret = gss_display_status_ptr(&new_stat,
  190. maj_stat,
  191. GSS_C_GSS_CODE,
  192. GSS_C_NULL_OID,
  193. &msg_ctx,
  194. &status1_string);
  195. errorStr.Append((const char *) status1_string.value, status1_string.length);
  196. gss_release_buffer_ptr(&new_stat, &status1_string);
  197. errorStr += '\n';
  198. ret = gss_display_status_ptr(&new_stat,
  199. min_stat,
  200. GSS_C_MECH_CODE,
  201. GSS_C_NULL_OID,
  202. &msg_ctx,
  203. &status2_string);
  204. errorStr.Append((const char *) status2_string.value, status2_string.length);
  205. errorStr += '\n';
  206. } while (!GSS_ERROR(ret) && msg_ctx != 0);
  207. LOG(("%s\n", errorStr.get()));
  208. }
  209. //-----------------------------------------------------------------------------
  210. nsAuthGSSAPI::nsAuthGSSAPI(pType package)
  211. : mServiceFlags(REQ_DEFAULT)
  212. {
  213. OM_uint32 minstat;
  214. OM_uint32 majstat;
  215. gss_OID_set mech_set;
  216. gss_OID item;
  217. unsigned int i;
  218. static gss_OID_desc gss_krb5_mech_oid_desc =
  219. { 9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
  220. static gss_OID_desc gss_spnego_mech_oid_desc =
  221. { 6, (void *) "\x2b\x06\x01\x05\x05\x02" };
  222. LOG(("entering nsAuthGSSAPI::nsAuthGSSAPI()\n"));
  223. mComplete = false;
  224. if (!gssLibrary && NS_FAILED(gssInit()))
  225. return;
  226. mCtx = GSS_C_NO_CONTEXT;
  227. mMechOID = &gss_krb5_mech_oid_desc;
  228. // if the type is kerberos we accept it as default
  229. // and exit
  230. if (package == PACKAGE_TYPE_KERBEROS)
  231. return;
  232. // Now, look at the list of supported mechanisms,
  233. // if SPNEGO is found, then use it.
  234. // Otherwise, set the desired mechanism to
  235. // GSS_C_NO_OID and let the system try to use
  236. // the default mechanism.
  237. //
  238. // Using Kerberos directly (instead of negotiating
  239. // with SPNEGO) may work in some cases depending
  240. // on how smart the server side is.
  241. majstat = gss_indicate_mechs_ptr(&minstat, &mech_set);
  242. if (GSS_ERROR(majstat))
  243. return;
  244. if (mech_set) {
  245. for (i=0; i<mech_set->count; i++) {
  246. item = &mech_set->elements[i];
  247. if (item->length == gss_spnego_mech_oid_desc.length &&
  248. !memcmp(item->elements, gss_spnego_mech_oid_desc.elements,
  249. item->length)) {
  250. // ok, we found it
  251. mMechOID = &gss_spnego_mech_oid_desc;
  252. break;
  253. }
  254. }
  255. gss_release_oid_set_ptr(&minstat, &mech_set);
  256. }
  257. }
  258. void
  259. nsAuthGSSAPI::Reset()
  260. {
  261. if (gssLibrary && mCtx != GSS_C_NO_CONTEXT) {
  262. OM_uint32 minor_status;
  263. gss_delete_sec_context_ptr(&minor_status, &mCtx, GSS_C_NO_BUFFER);
  264. }
  265. mCtx = GSS_C_NO_CONTEXT;
  266. mComplete = false;
  267. }
  268. /* static */ void
  269. nsAuthGSSAPI::Shutdown()
  270. {
  271. if (gssLibrary) {
  272. PR_UnloadLibrary(gssLibrary);
  273. gssLibrary = nullptr;
  274. }
  275. }
  276. /* Limitations apply to this class's thread safety. See the header file */
  277. NS_IMPL_ISUPPORTS(nsAuthGSSAPI, nsIAuthModule)
  278. NS_IMETHODIMP
  279. nsAuthGSSAPI::Init(const char *serviceName,
  280. uint32_t serviceFlags,
  281. const char16_t *domain,
  282. const char16_t *username,
  283. const char16_t *password)
  284. {
  285. // we don't expect to be passed any user credentials
  286. NS_ASSERTION(!domain && !username && !password, "unexpected credentials");
  287. // it's critial that the caller supply a service name to be used
  288. NS_ENSURE_TRUE(serviceName && *serviceName, NS_ERROR_INVALID_ARG);
  289. LOG(("entering nsAuthGSSAPI::Init()\n"));
  290. if (!gssLibrary)
  291. return NS_ERROR_NOT_INITIALIZED;
  292. mServiceName = serviceName;
  293. mServiceFlags = serviceFlags;
  294. return NS_OK;
  295. }
  296. NS_IMETHODIMP
  297. nsAuthGSSAPI::GetNextToken(const void *inToken,
  298. uint32_t inTokenLen,
  299. void **outToken,
  300. uint32_t *outTokenLen)
  301. {
  302. OM_uint32 major_status, minor_status;
  303. OM_uint32 req_flags = 0;
  304. gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
  305. gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
  306. gss_buffer_t in_token_ptr = GSS_C_NO_BUFFER;
  307. gss_name_t server;
  308. nsAutoCString userbuf;
  309. nsresult rv;
  310. LOG(("entering nsAuthGSSAPI::GetNextToken()\n"));
  311. if (!gssLibrary)
  312. return NS_ERROR_NOT_INITIALIZED;
  313. // If they've called us again after we're complete, reset to start afresh.
  314. if (mComplete)
  315. Reset();
  316. if (mServiceFlags & REQ_DELEGATE)
  317. req_flags |= GSS_C_DELEG_FLAG;
  318. if (mServiceFlags & REQ_MUTUAL_AUTH)
  319. req_flags |= GSS_C_MUTUAL_FLAG;
  320. input_token.value = (void *)mServiceName.get();
  321. input_token.length = mServiceName.Length() + 1;
  322. #if defined(HAVE_RES_NINIT)
  323. res_ninit(&_res);
  324. #endif
  325. major_status = gss_import_name_ptr(&minor_status,
  326. &input_token,
  327. &gss_c_nt_hostbased_service,
  328. &server);
  329. input_token.value = nullptr;
  330. input_token.length = 0;
  331. if (GSS_ERROR(major_status)) {
  332. LogGssError(major_status, minor_status, "gss_import_name() failed");
  333. return NS_ERROR_FAILURE;
  334. }
  335. if (inToken) {
  336. input_token.length = inTokenLen;
  337. input_token.value = (void *) inToken;
  338. in_token_ptr = &input_token;
  339. }
  340. else if (mCtx != GSS_C_NO_CONTEXT) {
  341. // If there is no input token, then we are starting a new
  342. // authentication sequence. If we have already initialized our
  343. // security context, then we're in trouble because it means that the
  344. // first sequence failed. We need to bail or else we might end up in
  345. // an infinite loop.
  346. LOG(("Cannot restart authentication sequence!"));
  347. return NS_ERROR_UNEXPECTED;
  348. }
  349. major_status = gss_init_sec_context_ptr(&minor_status,
  350. GSS_C_NO_CREDENTIAL,
  351. &mCtx,
  352. server,
  353. mMechOID,
  354. req_flags,
  355. GSS_C_INDEFINITE,
  356. GSS_C_NO_CHANNEL_BINDINGS,
  357. in_token_ptr,
  358. nullptr,
  359. &output_token,
  360. nullptr,
  361. nullptr);
  362. if (GSS_ERROR(major_status)) {
  363. LogGssError(major_status, minor_status, "gss_init_sec_context() failed");
  364. Reset();
  365. rv = NS_ERROR_FAILURE;
  366. goto end;
  367. }
  368. if (major_status == GSS_S_COMPLETE) {
  369. // Mark ourselves as being complete, so that if we're called again
  370. // we know to start afresh.
  371. mComplete = true;
  372. }
  373. else if (major_status == GSS_S_CONTINUE_NEEDED) {
  374. //
  375. // The important thing is that we do NOT reset the
  376. // context here because it will be needed on the
  377. // next call.
  378. //
  379. }
  380. *outTokenLen = output_token.length;
  381. if (output_token.length != 0)
  382. *outToken = nsMemory::Clone(output_token.value, output_token.length);
  383. else
  384. *outToken = nullptr;
  385. gss_release_buffer_ptr(&minor_status, &output_token);
  386. if (major_status == GSS_S_COMPLETE)
  387. rv = NS_SUCCESS_AUTH_FINISHED;
  388. else
  389. rv = NS_OK;
  390. end:
  391. gss_release_name_ptr(&minor_status, &server);
  392. LOG((" leaving nsAuthGSSAPI::GetNextToken [rv=%x]", rv));
  393. return rv;
  394. }
  395. NS_IMETHODIMP
  396. nsAuthGSSAPI::Unwrap(const void *inToken,
  397. uint32_t inTokenLen,
  398. void **outToken,
  399. uint32_t *outTokenLen)
  400. {
  401. OM_uint32 major_status, minor_status;
  402. gss_buffer_desc input_token;
  403. gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
  404. input_token.value = (void *) inToken;
  405. input_token.length = inTokenLen;
  406. major_status = gss_unwrap_ptr(&minor_status,
  407. mCtx,
  408. &input_token,
  409. &output_token,
  410. nullptr,
  411. nullptr);
  412. if (GSS_ERROR(major_status)) {
  413. LogGssError(major_status, minor_status, "gss_unwrap() failed");
  414. Reset();
  415. gss_release_buffer_ptr(&minor_status, &output_token);
  416. return NS_ERROR_FAILURE;
  417. }
  418. *outTokenLen = output_token.length;
  419. if (output_token.length)
  420. *outToken = nsMemory::Clone(output_token.value, output_token.length);
  421. else
  422. *outToken = nullptr;
  423. gss_release_buffer_ptr(&minor_status, &output_token);
  424. return NS_OK;
  425. }
  426. NS_IMETHODIMP
  427. nsAuthGSSAPI::Wrap(const void *inToken,
  428. uint32_t inTokenLen,
  429. bool confidential,
  430. void **outToken,
  431. uint32_t *outTokenLen)
  432. {
  433. OM_uint32 major_status, minor_status;
  434. gss_buffer_desc input_token;
  435. gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
  436. input_token.value = (void *) inToken;
  437. input_token.length = inTokenLen;
  438. major_status = gss_wrap_ptr(&minor_status,
  439. mCtx,
  440. confidential,
  441. GSS_C_QOP_DEFAULT,
  442. &input_token,
  443. nullptr,
  444. &output_token);
  445. if (GSS_ERROR(major_status)) {
  446. LogGssError(major_status, minor_status, "gss_wrap() failed");
  447. Reset();
  448. gss_release_buffer_ptr(&minor_status, &output_token);
  449. return NS_ERROR_FAILURE;
  450. }
  451. *outTokenLen = output_token.length;
  452. /* it is not possible for output_token.length to be zero */
  453. *outToken = nsMemory::Clone(output_token.value, output_token.length);
  454. gss_release_buffer_ptr(&minor_status, &output_token);
  455. return NS_OK;
  456. }