/contrib/bind9/lib/dns/gssapictx.c

https://bitbucket.org/freebsd/freebsd-head/ · C · 868 lines · 622 code · 111 blank · 135 comment · 159 complexity · 5fbdd061ca3c43dc87f3aaebe3f491e9 MD5 · raw file

  1. /*
  2. * Copyright (C) 2004-2012 Internet Systems Consortium, Inc. ("ISC")
  3. * Copyright (C) 2000, 2001 Internet Software Consortium.
  4. *
  5. * Permission to use, copy, modify, and/or distribute this software for any
  6. * purpose with or without fee is hereby granted, provided that the above
  7. * copyright notice and this permission notice appear in all copies.
  8. *
  9. * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  10. * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  11. * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  12. * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  13. * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  14. * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  15. * PERFORMANCE OF THIS SOFTWARE.
  16. */
  17. /* $Id$ */
  18. #include <config.h>
  19. #include <ctype.h>
  20. #include <stdlib.h>
  21. #include <string.h>
  22. #include <isc/buffer.h>
  23. #include <isc/dir.h>
  24. #include <isc/entropy.h>
  25. #include <isc/file.h>
  26. #include <isc/lex.h>
  27. #include <isc/mem.h>
  28. #include <isc/once.h>
  29. #include <isc/print.h>
  30. #include <isc/platform.h>
  31. #include <isc/random.h>
  32. #include <isc/string.h>
  33. #include <isc/time.h>
  34. #include <isc/util.h>
  35. #include <dns/fixedname.h>
  36. #include <dns/name.h>
  37. #include <dns/rdata.h>
  38. #include <dns/rdataclass.h>
  39. #include <dns/result.h>
  40. #include <dns/types.h>
  41. #include <dns/keyvalues.h>
  42. #include <dns/log.h>
  43. #include <dst/gssapi.h>
  44. #include <dst/result.h>
  45. #include "dst_internal.h"
  46. /*
  47. * If we're using our own SPNEGO implementation (see configure.in),
  48. * pull it in now. Otherwise, we just use whatever GSSAPI supplies.
  49. */
  50. #if defined(GSSAPI) && defined(USE_ISC_SPNEGO)
  51. #include "spnego.h"
  52. #define gss_accept_sec_context gss_accept_sec_context_spnego
  53. #define gss_init_sec_context gss_init_sec_context_spnego
  54. #endif
  55. /*
  56. * Solaris8 apparently needs an explicit OID set, and Solaris10 needs
  57. * one for anything but Kerberos. Supplying an explicit OID set
  58. * doesn't appear to hurt anything in other implementations, so we
  59. * always use one. If we're not using our own SPNEGO implementation,
  60. * we include SPNEGO's OID.
  61. */
  62. #if defined(GSSAPI)
  63. #include ISC_PLATFORM_KRB5HEADER
  64. static unsigned char krb5_mech_oid_bytes[] = {
  65. 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x01, 0x02, 0x02
  66. };
  67. #ifndef USE_ISC_SPNEGO
  68. static unsigned char spnego_mech_oid_bytes[] = {
  69. 0x2b, 0x06, 0x01, 0x05, 0x05, 0x02
  70. };
  71. #endif
  72. static gss_OID_desc mech_oid_set_array[] = {
  73. { sizeof(krb5_mech_oid_bytes), krb5_mech_oid_bytes },
  74. #ifndef USE_ISC_SPNEGO
  75. { sizeof(spnego_mech_oid_bytes), spnego_mech_oid_bytes },
  76. #endif
  77. };
  78. static gss_OID_set_desc mech_oid_set = {
  79. sizeof(mech_oid_set_array) / sizeof(*mech_oid_set_array),
  80. mech_oid_set_array
  81. };
  82. #endif
  83. #define REGION_TO_GBUFFER(r, gb) \
  84. do { \
  85. (gb).length = (r).length; \
  86. (gb).value = (r).base; \
  87. } while (0)
  88. #define GBUFFER_TO_REGION(gb, r) \
  89. do { \
  90. (r).length = (gb).length; \
  91. (r).base = (gb).value; \
  92. } while (0)
  93. #define RETERR(x) do { \
  94. result = (x); \
  95. if (result != ISC_R_SUCCESS) \
  96. goto out; \
  97. } while (0)
  98. #ifdef GSSAPI
  99. static inline void
  100. name_to_gbuffer(dns_name_t *name, isc_buffer_t *buffer,
  101. gss_buffer_desc *gbuffer)
  102. {
  103. dns_name_t tname, *namep;
  104. isc_region_t r;
  105. isc_result_t result;
  106. if (!dns_name_isabsolute(name))
  107. namep = name;
  108. else
  109. {
  110. unsigned int labels;
  111. dns_name_init(&tname, NULL);
  112. labels = dns_name_countlabels(name);
  113. dns_name_getlabelsequence(name, 0, labels - 1, &tname);
  114. namep = &tname;
  115. }
  116. result = dns_name_toprincipal(namep, buffer);
  117. RUNTIME_CHECK(result == ISC_R_SUCCESS);
  118. isc_buffer_putuint8(buffer, 0);
  119. isc_buffer_usedregion(buffer, &r);
  120. REGION_TO_GBUFFER(r, *gbuffer);
  121. }
  122. static void
  123. log_cred(const gss_cred_id_t cred) {
  124. OM_uint32 gret, minor, lifetime;
  125. gss_name_t gname;
  126. gss_buffer_desc gbuffer;
  127. gss_cred_usage_t usage;
  128. const char *usage_text;
  129. char buf[1024];
  130. gret = gss_inquire_cred(&minor, cred, &gname, &lifetime, &usage, NULL);
  131. if (gret != GSS_S_COMPLETE) {
  132. gss_log(3, "failed gss_inquire_cred: %s",
  133. gss_error_tostring(gret, minor, buf, sizeof(buf)));
  134. return;
  135. }
  136. gret = gss_display_name(&minor, gname, &gbuffer, NULL);
  137. if (gret != GSS_S_COMPLETE)
  138. gss_log(3, "failed gss_display_name: %s",
  139. gss_error_tostring(gret, minor, buf, sizeof(buf)));
  140. else {
  141. switch (usage) {
  142. case GSS_C_BOTH:
  143. usage_text = "GSS_C_BOTH";
  144. break;
  145. case GSS_C_INITIATE:
  146. usage_text = "GSS_C_INITIATE";
  147. break;
  148. case GSS_C_ACCEPT:
  149. usage_text = "GSS_C_ACCEPT";
  150. break;
  151. default:
  152. usage_text = "???";
  153. }
  154. gss_log(3, "gss cred: \"%s\", %s, %lu", (char *)gbuffer.value,
  155. usage_text, (unsigned long)lifetime);
  156. }
  157. if (gret == GSS_S_COMPLETE) {
  158. if (gbuffer.length != 0U) {
  159. gret = gss_release_buffer(&minor, &gbuffer);
  160. if (gret != GSS_S_COMPLETE)
  161. gss_log(3, "failed gss_release_buffer: %s",
  162. gss_error_tostring(gret, minor, buf,
  163. sizeof(buf)));
  164. }
  165. }
  166. gret = gss_release_name(&minor, &gname);
  167. if (gret != GSS_S_COMPLETE)
  168. gss_log(3, "failed gss_release_name: %s",
  169. gss_error_tostring(gret, minor, buf, sizeof(buf)));
  170. }
  171. #endif
  172. #ifdef GSSAPI
  173. /*
  174. * check for the most common configuration errors.
  175. *
  176. * The errors checked for are:
  177. * - tkey-gssapi-credential doesn't start with DNS/
  178. * - the default realm in /etc/krb5.conf and the
  179. * tkey-gssapi-credential bind config option don't match
  180. *
  181. * Note that if tkey-gssapi-keytab is set then these configure checks
  182. * are not performed, and runtime errors from gssapi are used instead
  183. */
  184. static void
  185. check_config(const char *gss_name) {
  186. const char *p;
  187. krb5_context krb5_ctx;
  188. char *krb5_realm = NULL;
  189. if (strncasecmp(gss_name, "DNS/", 4) != 0) {
  190. gss_log(ISC_LOG_ERROR, "tkey-gssapi-credential (%s) "
  191. "should start with 'DNS/'", gss_name);
  192. return;
  193. }
  194. if (krb5_init_context(&krb5_ctx) != 0) {
  195. gss_log(ISC_LOG_ERROR, "Unable to initialise krb5 context");
  196. return;
  197. }
  198. if (krb5_get_default_realm(krb5_ctx, &krb5_realm) != 0) {
  199. gss_log(ISC_LOG_ERROR, "Unable to get krb5 default realm");
  200. krb5_free_context(krb5_ctx);
  201. return;
  202. }
  203. p = strchr(gss_name, '/');
  204. if (p == NULL) {
  205. gss_log(ISC_LOG_ERROR, "badly formatted "
  206. "tkey-gssapi-credentials (%s)", gss_name);
  207. krb5_free_context(krb5_ctx);
  208. return;
  209. }
  210. if (strcasecmp(p + 1, krb5_realm) != 0) {
  211. gss_log(ISC_LOG_ERROR, "default realm from krb5.conf (%s) "
  212. "does not match tkey-gssapi-credential (%s)",
  213. krb5_realm, gss_name);
  214. krb5_free_context(krb5_ctx);
  215. return;
  216. }
  217. krb5_free_context(krb5_ctx);
  218. }
  219. #endif
  220. isc_result_t
  221. dst_gssapi_acquirecred(dns_name_t *name, isc_boolean_t initiate,
  222. gss_cred_id_t *cred)
  223. {
  224. #ifdef GSSAPI
  225. isc_buffer_t namebuf;
  226. gss_name_t gname;
  227. gss_buffer_desc gnamebuf;
  228. unsigned char array[DNS_NAME_MAXTEXT + 1];
  229. OM_uint32 gret, minor;
  230. gss_OID_set mechs;
  231. OM_uint32 lifetime;
  232. gss_cred_usage_t usage;
  233. char buf[1024];
  234. REQUIRE(cred != NULL && *cred == NULL);
  235. /*
  236. * XXXSRA In theory we could use GSS_C_NT_HOSTBASED_SERVICE
  237. * here when we're in the acceptor role, which would let us
  238. * default the hostname and use a compiled in default service
  239. * name of "DNS", giving one less thing to configure in
  240. * named.conf. Unfortunately, this creates a circular
  241. * dependency due to DNS-based realm lookup in at least one
  242. * GSSAPI implementation (Heimdal). Oh well.
  243. */
  244. if (name != NULL) {
  245. isc_buffer_init(&namebuf, array, sizeof(array));
  246. name_to_gbuffer(name, &namebuf, &gnamebuf);
  247. gret = gss_import_name(&minor, &gnamebuf,
  248. GSS_C_NO_OID, &gname);
  249. if (gret != GSS_S_COMPLETE) {
  250. check_config((char *)array);
  251. gss_log(3, "failed gss_import_name: %s",
  252. gss_error_tostring(gret, minor, buf,
  253. sizeof(buf)));
  254. return (ISC_R_FAILURE);
  255. }
  256. } else
  257. gname = NULL;
  258. /* Get the credentials. */
  259. if (gname != NULL)
  260. gss_log(3, "acquiring credentials for %s",
  261. (char *)gnamebuf.value);
  262. else {
  263. /* XXXDCL does this even make any sense? */
  264. gss_log(3, "acquiring credentials for ?");
  265. }
  266. if (initiate)
  267. usage = GSS_C_INITIATE;
  268. else
  269. usage = GSS_C_ACCEPT;
  270. gret = gss_acquire_cred(&minor, gname, GSS_C_INDEFINITE,
  271. &mech_oid_set,
  272. usage, cred, &mechs, &lifetime);
  273. if (gret != GSS_S_COMPLETE) {
  274. gss_log(3, "failed to acquire %s credentials for %s: %s",
  275. initiate ? "initiate" : "accept",
  276. (gname != NULL) ? (char *)gnamebuf.value : "?",
  277. gss_error_tostring(gret, minor, buf, sizeof(buf)));
  278. check_config((char *)array);
  279. return (ISC_R_FAILURE);
  280. }
  281. gss_log(4, "acquired %s credentials for %s",
  282. initiate ? "initiate" : "accept",
  283. (gname != NULL) ? (char *)gnamebuf.value : "?");
  284. log_cred(*cred);
  285. return (ISC_R_SUCCESS);
  286. #else
  287. REQUIRE(cred != NULL && *cred == NULL);
  288. UNUSED(name);
  289. UNUSED(initiate);
  290. UNUSED(cred);
  291. return (ISC_R_NOTIMPLEMENTED);
  292. #endif
  293. }
  294. isc_boolean_t
  295. dst_gssapi_identitymatchesrealmkrb5(dns_name_t *signer, dns_name_t *name,
  296. dns_name_t *realm)
  297. {
  298. #ifdef GSSAPI
  299. char sbuf[DNS_NAME_FORMATSIZE];
  300. char nbuf[DNS_NAME_FORMATSIZE];
  301. char rbuf[DNS_NAME_FORMATSIZE];
  302. char *sname;
  303. char *rname;
  304. isc_buffer_t buffer;
  305. isc_result_t result;
  306. /*
  307. * It is far, far easier to write the names we are looking at into
  308. * a string, and do string operations on them.
  309. */
  310. isc_buffer_init(&buffer, sbuf, sizeof(sbuf));
  311. result = dns_name_toprincipal(signer, &buffer);
  312. RUNTIME_CHECK(result == ISC_R_SUCCESS);
  313. isc_buffer_putuint8(&buffer, 0);
  314. if (name != NULL)
  315. dns_name_format(name, nbuf, sizeof(nbuf));
  316. dns_name_format(realm, rbuf, sizeof(rbuf));
  317. /*
  318. * Find the realm portion. This is the part after the @. If it
  319. * does not exist, we don't have something we like, so we fail our
  320. * compare.
  321. */
  322. rname = strchr(sbuf, '@');
  323. if (rname == NULL)
  324. return (isc_boolean_false);
  325. *rname = '\0';
  326. rname++;
  327. /*
  328. * Find the host portion of the signer's name. We do this by
  329. * searching for the first / character. We then check to make
  330. * certain the instance name is "host"
  331. *
  332. * This will work for
  333. * host/example.com@EXAMPLE.COM
  334. */
  335. sname = strchr(sbuf, '/');
  336. if (sname == NULL)
  337. return (isc_boolean_false);
  338. *sname = '\0';
  339. sname++;
  340. if (strcmp(sbuf, "host") != 0)
  341. return (isc_boolean_false);
  342. /*
  343. * Now, we do a simple comparison between the name and the realm.
  344. */
  345. if (name != NULL) {
  346. if ((strcasecmp(sname, nbuf) == 0)
  347. && (strcmp(rname, rbuf) == 0))
  348. return (isc_boolean_true);
  349. } else {
  350. if (strcmp(rname, rbuf) == 0)
  351. return (isc_boolean_true);
  352. }
  353. return (isc_boolean_false);
  354. #else
  355. UNUSED(signer);
  356. UNUSED(name);
  357. UNUSED(realm);
  358. return (isc_boolean_false);
  359. #endif
  360. }
  361. isc_boolean_t
  362. dst_gssapi_identitymatchesrealmms(dns_name_t *signer, dns_name_t *name,
  363. dns_name_t *realm)
  364. {
  365. #ifdef GSSAPI
  366. char sbuf[DNS_NAME_FORMATSIZE];
  367. char nbuf[DNS_NAME_FORMATSIZE];
  368. char rbuf[DNS_NAME_FORMATSIZE];
  369. char *sname;
  370. char *nname;
  371. char *rname;
  372. isc_buffer_t buffer;
  373. isc_result_t result;
  374. /*
  375. * It is far, far easier to write the names we are looking at into
  376. * a string, and do string operations on them.
  377. */
  378. isc_buffer_init(&buffer, sbuf, sizeof(sbuf));
  379. result = dns_name_toprincipal(signer, &buffer);
  380. RUNTIME_CHECK(result == ISC_R_SUCCESS);
  381. isc_buffer_putuint8(&buffer, 0);
  382. if (name != NULL)
  383. dns_name_format(name, nbuf, sizeof(nbuf));
  384. dns_name_format(realm, rbuf, sizeof(rbuf));
  385. /*
  386. * Find the realm portion. This is the part after the @. If it
  387. * does not exist, we don't have something we like, so we fail our
  388. * compare.
  389. */
  390. rname = strchr(sbuf, '@');
  391. if (rname == NULL)
  392. return (isc_boolean_false);
  393. sname = strchr(sbuf, '$');
  394. if (sname == NULL)
  395. return (isc_boolean_false);
  396. /*
  397. * Verify that the $ and @ follow one another.
  398. */
  399. if (rname - sname != 1)
  400. return (isc_boolean_false);
  401. /*
  402. * Find the host portion of the signer's name. Zero out the $ so
  403. * it terminates the signer's name, and skip past the @ for
  404. * the realm.
  405. *
  406. * All service principals in Microsoft format seem to be in
  407. * machinename$@EXAMPLE.COM
  408. * format.
  409. */
  410. rname++;
  411. *sname = '\0';
  412. sname = sbuf;
  413. /*
  414. * Find the first . in the target name, and make it the end of
  415. * the string. The rest of the name has to match the realm.
  416. */
  417. if (name != NULL) {
  418. nname = strchr(nbuf, '.');
  419. if (nname == NULL)
  420. return (isc_boolean_false);
  421. *nname++ = '\0';
  422. }
  423. /*
  424. * Now, we do a simple comparison between the name and the realm.
  425. */
  426. if (name != NULL) {
  427. if ((strcasecmp(sname, nbuf) == 0)
  428. && (strcmp(rname, rbuf) == 0)
  429. && (strcasecmp(nname, rbuf) == 0))
  430. return (isc_boolean_true);
  431. } else {
  432. if (strcmp(rname, rbuf) == 0)
  433. return (isc_boolean_true);
  434. }
  435. return (isc_boolean_false);
  436. #else
  437. UNUSED(signer);
  438. UNUSED(name);
  439. UNUSED(realm);
  440. return (isc_boolean_false);
  441. #endif
  442. }
  443. isc_result_t
  444. dst_gssapi_releasecred(gss_cred_id_t *cred) {
  445. #ifdef GSSAPI
  446. OM_uint32 gret, minor;
  447. char buf[1024];
  448. REQUIRE(cred != NULL && *cred != NULL);
  449. gret = gss_release_cred(&minor, cred);
  450. if (gret != GSS_S_COMPLETE) {
  451. /* Log the error, but still free the credential's memory */
  452. gss_log(3, "failed releasing credential: %s",
  453. gss_error_tostring(gret, minor, buf, sizeof(buf)));
  454. }
  455. *cred = NULL;
  456. return(ISC_R_SUCCESS);
  457. #else
  458. UNUSED(cred);
  459. return (ISC_R_NOTIMPLEMENTED);
  460. #endif
  461. }
  462. #ifdef GSSAPI
  463. /*
  464. * Format a gssapi error message info into a char ** on the given memory
  465. * context. This is used to return gssapi error messages back up the
  466. * call chain for reporting to the user.
  467. */
  468. static void
  469. gss_err_message(isc_mem_t *mctx, isc_uint32_t major, isc_uint32_t minor,
  470. char **err_message)
  471. {
  472. char buf[1024];
  473. char *estr;
  474. if (err_message == NULL || mctx == NULL) {
  475. /* the caller doesn't want any error messages */
  476. return;
  477. }
  478. estr = gss_error_tostring(major, minor, buf, sizeof(buf));
  479. if (estr)
  480. (*err_message) = isc_mem_strdup(mctx, estr);
  481. }
  482. #endif
  483. isc_result_t
  484. dst_gssapi_initctx(dns_name_t *name, isc_buffer_t *intoken,
  485. isc_buffer_t *outtoken, gss_ctx_id_t *gssctx,
  486. isc_mem_t *mctx, char **err_message)
  487. {
  488. #ifdef GSSAPI
  489. isc_region_t r;
  490. isc_buffer_t namebuf;
  491. gss_name_t gname;
  492. OM_uint32 gret, minor, ret_flags, flags;
  493. gss_buffer_desc gintoken, *gintokenp, gouttoken = GSS_C_EMPTY_BUFFER;
  494. isc_result_t result;
  495. gss_buffer_desc gnamebuf;
  496. unsigned char array[DNS_NAME_MAXTEXT + 1];
  497. /* Client must pass us a valid gss_ctx_id_t here */
  498. REQUIRE(gssctx != NULL);
  499. REQUIRE(mctx != NULL);
  500. isc_buffer_init(&namebuf, array, sizeof(array));
  501. name_to_gbuffer(name, &namebuf, &gnamebuf);
  502. /* Get the name as a GSS name */
  503. gret = gss_import_name(&minor, &gnamebuf, GSS_C_NO_OID, &gname);
  504. if (gret != GSS_S_COMPLETE) {
  505. gss_err_message(mctx, gret, minor, err_message);
  506. result = ISC_R_FAILURE;
  507. goto out;
  508. }
  509. if (intoken != NULL) {
  510. /* Don't call gss_release_buffer for gintoken! */
  511. REGION_TO_GBUFFER(*intoken, gintoken);
  512. gintokenp = &gintoken;
  513. } else {
  514. gintokenp = NULL;
  515. }
  516. /*
  517. * Note that we don't set GSS_C_SEQUENCE_FLAG as Windows DNS
  518. * servers don't like it.
  519. */
  520. flags = GSS_C_REPLAY_FLAG | GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG;
  521. gret = gss_init_sec_context(&minor, GSS_C_NO_CREDENTIAL, gssctx,
  522. gname, GSS_SPNEGO_MECHANISM, flags,
  523. 0, NULL, gintokenp,
  524. NULL, &gouttoken, &ret_flags, NULL);
  525. if (gret != GSS_S_COMPLETE && gret != GSS_S_CONTINUE_NEEDED) {
  526. gss_err_message(mctx, gret, minor, err_message);
  527. gss_log(3, "Failure initiating security context: %s",
  528. *err_message);
  529. result = ISC_R_FAILURE;
  530. goto out;
  531. }
  532. /*
  533. * XXXSRA Not handled yet: RFC 3645 3.1.1: check ret_flags
  534. * MUTUAL and INTEG flags, fail if either not set.
  535. */
  536. /*
  537. * RFC 2744 states the a valid output token has a non-zero length.
  538. */
  539. if (gouttoken.length != 0U) {
  540. GBUFFER_TO_REGION(gouttoken, r);
  541. RETERR(isc_buffer_copyregion(outtoken, &r));
  542. (void)gss_release_buffer(&minor, &gouttoken);
  543. }
  544. (void)gss_release_name(&minor, &gname);
  545. if (gret == GSS_S_COMPLETE)
  546. result = ISC_R_SUCCESS;
  547. else
  548. result = DNS_R_CONTINUE;
  549. out:
  550. return (result);
  551. #else
  552. UNUSED(name);
  553. UNUSED(intoken);
  554. UNUSED(outtoken);
  555. UNUSED(gssctx);
  556. UNUSED(mctx);
  557. UNUSED(err_message);
  558. return (ISC_R_NOTIMPLEMENTED);
  559. #endif
  560. }
  561. isc_result_t
  562. dst_gssapi_acceptctx(gss_cred_id_t cred,
  563. const char *gssapi_keytab,
  564. isc_region_t *intoken, isc_buffer_t **outtoken,
  565. gss_ctx_id_t *ctxout, dns_name_t *principal,
  566. isc_mem_t *mctx)
  567. {
  568. #ifdef GSSAPI
  569. isc_region_t r;
  570. isc_buffer_t namebuf;
  571. gss_buffer_desc gnamebuf = GSS_C_EMPTY_BUFFER, gintoken,
  572. gouttoken = GSS_C_EMPTY_BUFFER;
  573. OM_uint32 gret, minor;
  574. gss_ctx_id_t context = GSS_C_NO_CONTEXT;
  575. gss_name_t gname = NULL;
  576. isc_result_t result;
  577. char buf[1024];
  578. REQUIRE(outtoken != NULL && *outtoken == NULL);
  579. REGION_TO_GBUFFER(*intoken, gintoken);
  580. if (*ctxout == NULL)
  581. context = GSS_C_NO_CONTEXT;
  582. else
  583. context = *ctxout;
  584. if (gssapi_keytab != NULL) {
  585. #ifdef ISC_PLATFORM_GSSAPI_KRB5_HEADER
  586. gret = gsskrb5_register_acceptor_identity(gssapi_keytab);
  587. if (gret != GSS_S_COMPLETE) {
  588. gss_log(3, "failed "
  589. "gsskrb5_register_acceptor_identity(%s): %s",
  590. gssapi_keytab,
  591. gss_error_tostring(gret, 0, buf, sizeof(buf)));
  592. return (DNS_R_INVALIDTKEY);
  593. }
  594. #else
  595. /*
  596. * Minimize memory leakage by only setting KRB5_KTNAME
  597. * if it needs to change.
  598. */
  599. const char *old = getenv("KRB5_KTNAME");
  600. if (old == NULL || strcmp(old, gssapi_keytab) != 0) {
  601. char *kt = malloc(strlen(gssapi_keytab) + 13);
  602. if (kt == NULL)
  603. return (ISC_R_NOMEMORY);
  604. sprintf(kt, "KRB5_KTNAME=%s", gssapi_keytab);
  605. if (putenv(kt) != 0)
  606. return (ISC_R_NOMEMORY);
  607. }
  608. #endif
  609. }
  610. log_cred(cred);
  611. gret = gss_accept_sec_context(&minor, &context, cred, &gintoken,
  612. GSS_C_NO_CHANNEL_BINDINGS, &gname,
  613. NULL, &gouttoken, NULL, NULL, NULL);
  614. result = ISC_R_FAILURE;
  615. switch (gret) {
  616. case GSS_S_COMPLETE:
  617. result = ISC_R_SUCCESS;
  618. break;
  619. case GSS_S_CONTINUE_NEEDED:
  620. result = DNS_R_CONTINUE;
  621. break;
  622. case GSS_S_DEFECTIVE_TOKEN:
  623. case GSS_S_DEFECTIVE_CREDENTIAL:
  624. case GSS_S_BAD_SIG:
  625. case GSS_S_DUPLICATE_TOKEN:
  626. case GSS_S_OLD_TOKEN:
  627. case GSS_S_NO_CRED:
  628. case GSS_S_CREDENTIALS_EXPIRED:
  629. case GSS_S_BAD_BINDINGS:
  630. case GSS_S_NO_CONTEXT:
  631. case GSS_S_BAD_MECH:
  632. case GSS_S_FAILURE:
  633. result = DNS_R_INVALIDTKEY;
  634. /* fall through */
  635. default:
  636. gss_log(3, "failed gss_accept_sec_context: %s",
  637. gss_error_tostring(gret, minor, buf, sizeof(buf)));
  638. return (result);
  639. }
  640. if (gouttoken.length > 0U) {
  641. RETERR(isc_buffer_allocate(mctx, outtoken, gouttoken.length));
  642. GBUFFER_TO_REGION(gouttoken, r);
  643. RETERR(isc_buffer_copyregion(*outtoken, &r));
  644. (void)gss_release_buffer(&minor, &gouttoken);
  645. }
  646. if (gret == GSS_S_COMPLETE) {
  647. gret = gss_display_name(&minor, gname, &gnamebuf, NULL);
  648. if (gret != GSS_S_COMPLETE) {
  649. gss_log(3, "failed gss_display_name: %s",
  650. gss_error_tostring(gret, minor,
  651. buf, sizeof(buf)));
  652. RETERR(ISC_R_FAILURE);
  653. }
  654. /*
  655. * Compensate for a bug in Solaris8's implementation
  656. * of gss_display_name(). Should be harmless in any
  657. * case, since principal names really should not
  658. * contain null characters.
  659. */
  660. if (gnamebuf.length > 0U &&
  661. ((char *)gnamebuf.value)[gnamebuf.length - 1] == '\0')
  662. gnamebuf.length--;
  663. gss_log(3, "gss-api source name (accept) is %.*s",
  664. (int)gnamebuf.length, (char *)gnamebuf.value);
  665. GBUFFER_TO_REGION(gnamebuf, r);
  666. isc_buffer_init(&namebuf, r.base, r.length);
  667. isc_buffer_add(&namebuf, r.length);
  668. RETERR(dns_name_fromtext(principal, &namebuf, dns_rootname,
  669. 0, NULL));
  670. if (gnamebuf.length != 0U) {
  671. gret = gss_release_buffer(&minor, &gnamebuf);
  672. if (gret != GSS_S_COMPLETE)
  673. gss_log(3, "failed gss_release_buffer: %s",
  674. gss_error_tostring(gret, minor, buf,
  675. sizeof(buf)));
  676. }
  677. }
  678. *ctxout = context;
  679. out:
  680. if (gname != NULL) {
  681. gret = gss_release_name(&minor, &gname);
  682. if (gret != GSS_S_COMPLETE)
  683. gss_log(3, "failed gss_release_name: %s",
  684. gss_error_tostring(gret, minor, buf,
  685. sizeof(buf)));
  686. }
  687. return (result);
  688. #else
  689. UNUSED(cred);
  690. UNUSED(gssapi_keytab);
  691. UNUSED(intoken);
  692. UNUSED(outtoken);
  693. UNUSED(ctxout);
  694. UNUSED(principal);
  695. UNUSED(mctx);
  696. return (ISC_R_NOTIMPLEMENTED);
  697. #endif
  698. }
  699. isc_result_t
  700. dst_gssapi_deletectx(isc_mem_t *mctx, gss_ctx_id_t *gssctx)
  701. {
  702. #ifdef GSSAPI
  703. OM_uint32 gret, minor;
  704. char buf[1024];
  705. UNUSED(mctx);
  706. REQUIRE(gssctx != NULL && *gssctx != NULL);
  707. /* Delete the context from the GSS provider */
  708. gret = gss_delete_sec_context(&minor, gssctx, GSS_C_NO_BUFFER);
  709. if (gret != GSS_S_COMPLETE) {
  710. /* Log the error, but still free the context's memory */
  711. gss_log(3, "Failure deleting security context %s",
  712. gss_error_tostring(gret, minor, buf, sizeof(buf)));
  713. }
  714. return(ISC_R_SUCCESS);
  715. #else
  716. UNUSED(mctx);
  717. UNUSED(gssctx);
  718. return (ISC_R_NOTIMPLEMENTED);
  719. #endif
  720. }
  721. char *
  722. gss_error_tostring(isc_uint32_t major, isc_uint32_t minor,
  723. char *buf, size_t buflen) {
  724. #ifdef GSSAPI
  725. gss_buffer_desc msg_minor = GSS_C_EMPTY_BUFFER,
  726. msg_major = GSS_C_EMPTY_BUFFER;
  727. OM_uint32 msg_ctx, minor_stat;
  728. /* Handle major status */
  729. msg_ctx = 0;
  730. (void)gss_display_status(&minor_stat, major, GSS_C_GSS_CODE,
  731. GSS_C_NULL_OID, &msg_ctx, &msg_major);
  732. /* Handle minor status */
  733. msg_ctx = 0;
  734. (void)gss_display_status(&minor_stat, minor, GSS_C_MECH_CODE,
  735. GSS_C_NULL_OID, &msg_ctx, &msg_minor);
  736. snprintf(buf, buflen, "GSSAPI error: Major = %s, Minor = %s.",
  737. (char *)msg_major.value, (char *)msg_minor.value);
  738. if (msg_major.length != 0U)
  739. (void)gss_release_buffer(&minor_stat, &msg_major);
  740. if (msg_minor.length != 0U)
  741. (void)gss_release_buffer(&minor_stat, &msg_minor);
  742. return(buf);
  743. #else
  744. snprintf(buf, buflen, "GSSAPI error: Major = %u, Minor = %u.",
  745. major, minor);
  746. return (buf);
  747. #endif
  748. }
  749. void
  750. gss_log(int level, const char *fmt, ...) {
  751. va_list ap;
  752. va_start(ap, fmt);
  753. isc_log_vwrite(dns_lctx, DNS_LOGCATEGORY_GENERAL,
  754. DNS_LOGMODULE_TKEY, ISC_LOG_DEBUG(level), fmt, ap);
  755. va_end(ap);
  756. }
  757. /*! \file */