gss-serv.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  1. /* $OpenBSD: gss-serv.c,v 1.32 2020/03/13 03:17:07 djm Exp $ */
  2. /*
  3. * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions
  7. * are met:
  8. * 1. Redistributions of source code must retain the above copyright
  9. * notice, this list of conditions and the following disclaimer.
  10. * 2. Redistributions in binary form must reproduce the above copyright
  11. * notice, this list of conditions and the following disclaimer in the
  12. * documentation and/or other materials provided with the distribution.
  13. *
  14. * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
  15. * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  16. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  17. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  18. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  19. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  20. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  21. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  22. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  23. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  24. */
  25. #include "includes.h"
  26. #ifdef GSSAPI
  27. #include <sys/types.h>
  28. #include <stdarg.h>
  29. #include <string.h>
  30. #include <unistd.h>
  31. #include "openbsd-compat/sys-queue.h"
  32. #include "xmalloc.h"
  33. #include "sshkey.h"
  34. #include "hostfile.h"
  35. #include "auth.h"
  36. #include "log.h"
  37. #include "channels.h"
  38. #include "session.h"
  39. #include "misc.h"
  40. #include "servconf.h"
  41. #include "ssh-gss.h"
  42. extern ServerOptions options;
  43. static ssh_gssapi_client gssapi_client =
  44. { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER,
  45. GSS_C_NO_CREDENTIAL, NULL, {NULL, NULL, NULL, NULL}};
  46. ssh_gssapi_mech gssapi_null_mech =
  47. { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL};
  48. #ifdef KRB5
  49. extern ssh_gssapi_mech gssapi_kerberos_mech;
  50. #endif
  51. ssh_gssapi_mech* supported_mechs[]= {
  52. #ifdef KRB5
  53. &gssapi_kerberos_mech,
  54. #endif
  55. &gssapi_null_mech,
  56. };
  57. /*
  58. * ssh_gssapi_supported_oids() can cause sandbox violations, so prepare the
  59. * list of supported mechanisms before privsep is set up.
  60. */
  61. static gss_OID_set supported_oids;
  62. void
  63. ssh_gssapi_prepare_supported_oids(void)
  64. {
  65. ssh_gssapi_supported_oids(&supported_oids);
  66. }
  67. OM_uint32
  68. ssh_gssapi_test_oid_supported(OM_uint32 *ms, gss_OID member, int *present)
  69. {
  70. if (supported_oids == NULL)
  71. ssh_gssapi_prepare_supported_oids();
  72. return gss_test_oid_set_member(ms, member, supported_oids, present);
  73. }
  74. /*
  75. * Acquire credentials for a server running on the current host.
  76. * Requires that the context structure contains a valid OID
  77. */
  78. /* Returns a GSSAPI error code */
  79. /* Privileged (called from ssh_gssapi_server_ctx) */
  80. static OM_uint32
  81. ssh_gssapi_acquire_cred(Gssctxt *ctx)
  82. {
  83. OM_uint32 status;
  84. char lname[NI_MAXHOST];
  85. gss_OID_set oidset;
  86. if (options.gss_strict_acceptor) {
  87. gss_create_empty_oid_set(&status, &oidset);
  88. gss_add_oid_set_member(&status, ctx->oid, &oidset);
  89. if (gethostname(lname, MAXHOSTNAMELEN)) {
  90. gss_release_oid_set(&status, &oidset);
  91. return (-1);
  92. }
  93. if (GSS_ERROR(ssh_gssapi_import_name(ctx, lname))) {
  94. gss_release_oid_set(&status, &oidset);
  95. return (ctx->major);
  96. }
  97. if ((ctx->major = gss_acquire_cred(&ctx->minor,
  98. ctx->name, 0, oidset, GSS_C_ACCEPT, &ctx->creds,
  99. NULL, NULL)))
  100. ssh_gssapi_error(ctx);
  101. gss_release_oid_set(&status, &oidset);
  102. return (ctx->major);
  103. } else {
  104. ctx->name = GSS_C_NO_NAME;
  105. ctx->creds = GSS_C_NO_CREDENTIAL;
  106. }
  107. return GSS_S_COMPLETE;
  108. }
  109. /* Privileged */
  110. OM_uint32
  111. ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID oid)
  112. {
  113. if (*ctx)
  114. ssh_gssapi_delete_ctx(ctx);
  115. ssh_gssapi_build_ctx(ctx);
  116. ssh_gssapi_set_oid(*ctx, oid);
  117. return (ssh_gssapi_acquire_cred(*ctx));
  118. }
  119. /* Unprivileged */
  120. void
  121. ssh_gssapi_supported_oids(gss_OID_set *oidset)
  122. {
  123. int i = 0;
  124. OM_uint32 min_status;
  125. int present;
  126. gss_OID_set supported;
  127. gss_create_empty_oid_set(&min_status, oidset);
  128. gss_indicate_mechs(&min_status, &supported);
  129. while (supported_mechs[i]->name != NULL) {
  130. if (GSS_ERROR(gss_test_oid_set_member(&min_status,
  131. &supported_mechs[i]->oid, supported, &present)))
  132. present = 0;
  133. if (present)
  134. gss_add_oid_set_member(&min_status,
  135. &supported_mechs[i]->oid, oidset);
  136. i++;
  137. }
  138. gss_release_oid_set(&min_status, &supported);
  139. }
  140. /* Wrapper around accept_sec_context
  141. * Requires that the context contains:
  142. * oid
  143. * credentials (from ssh_gssapi_acquire_cred)
  144. */
  145. /* Privileged */
  146. OM_uint32
  147. ssh_gssapi_accept_ctx(Gssctxt *ctx, gss_buffer_desc *recv_tok,
  148. gss_buffer_desc *send_tok, OM_uint32 *flags)
  149. {
  150. OM_uint32 status;
  151. gss_OID mech;
  152. ctx->major = gss_accept_sec_context(&ctx->minor,
  153. &ctx->context, ctx->creds, recv_tok,
  154. GSS_C_NO_CHANNEL_BINDINGS, &ctx->client, &mech,
  155. send_tok, flags, NULL, &ctx->client_creds);
  156. if (GSS_ERROR(ctx->major))
  157. ssh_gssapi_error(ctx);
  158. if (ctx->client_creds)
  159. debug("Received some client credentials");
  160. else
  161. debug("Got no client credentials");
  162. status = ctx->major;
  163. /* Now, if we're complete and we have the right flags, then
  164. * we flag the user as also having been authenticated
  165. */
  166. if (((flags == NULL) || ((*flags & GSS_C_MUTUAL_FLAG) &&
  167. (*flags & GSS_C_INTEG_FLAG))) && (ctx->major == GSS_S_COMPLETE)) {
  168. if (ssh_gssapi_getclient(ctx, &gssapi_client))
  169. fatal("Couldn't convert client name");
  170. }
  171. return (status);
  172. }
  173. /*
  174. * This parses an exported name, extracting the mechanism specific portion
  175. * to use for ACL checking. It verifies that the name belongs the mechanism
  176. * originally selected.
  177. */
  178. static OM_uint32
  179. ssh_gssapi_parse_ename(Gssctxt *ctx, gss_buffer_t ename, gss_buffer_t name)
  180. {
  181. u_char *tok;
  182. OM_uint32 offset;
  183. OM_uint32 oidl;
  184. tok = ename->value;
  185. /*
  186. * Check that ename is long enough for all of the fixed length
  187. * header, and that the initial ID bytes are correct
  188. */
  189. if (ename->length < 6 || memcmp(tok, "\x04\x01", 2) != 0)
  190. return GSS_S_FAILURE;
  191. /*
  192. * Extract the OID, and check it. Here GSSAPI breaks with tradition
  193. * and does use the OID type and length bytes. To confuse things
  194. * there are two lengths - the first including these, and the
  195. * second without.
  196. */
  197. oidl = get_u16(tok+2); /* length including next two bytes */
  198. oidl = oidl-2; /* turn it into the _real_ length of the variable OID */
  199. /*
  200. * Check the BER encoding for correct type and length, that the
  201. * string is long enough and that the OID matches that in our context
  202. */
  203. if (tok[4] != 0x06 || tok[5] != oidl ||
  204. ename->length < oidl+6 ||
  205. !ssh_gssapi_check_oid(ctx, tok+6, oidl))
  206. return GSS_S_FAILURE;
  207. offset = oidl+6;
  208. if (ename->length < offset+4)
  209. return GSS_S_FAILURE;
  210. name->length = get_u32(tok+offset);
  211. offset += 4;
  212. if (UINT_MAX - offset < name->length)
  213. return GSS_S_FAILURE;
  214. if (ename->length < offset+name->length)
  215. return GSS_S_FAILURE;
  216. name->value = xmalloc(name->length+1);
  217. memcpy(name->value, tok+offset, name->length);
  218. ((char *)name->value)[name->length] = 0;
  219. return GSS_S_COMPLETE;
  220. }
  221. /* Extract the client details from a given context. This can only reliably
  222. * be called once for a context */
  223. /* Privileged (called from accept_secure_ctx) */
  224. OM_uint32
  225. ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
  226. {
  227. int i = 0;
  228. gss_buffer_desc ename;
  229. client->mech = NULL;
  230. while (supported_mechs[i]->name != NULL) {
  231. if (supported_mechs[i]->oid.length == ctx->oid->length &&
  232. (memcmp(supported_mechs[i]->oid.elements,
  233. ctx->oid->elements, ctx->oid->length) == 0))
  234. client->mech = supported_mechs[i];
  235. i++;
  236. }
  237. if (client->mech == NULL)
  238. return GSS_S_FAILURE;
  239. if ((ctx->major = gss_display_name(&ctx->minor, ctx->client,
  240. &client->displayname, NULL))) {
  241. ssh_gssapi_error(ctx);
  242. return (ctx->major);
  243. }
  244. if ((ctx->major = gss_export_name(&ctx->minor, ctx->client,
  245. &ename))) {
  246. ssh_gssapi_error(ctx);
  247. return (ctx->major);
  248. }
  249. if ((ctx->major = ssh_gssapi_parse_ename(ctx,&ename,
  250. &client->exportedname))) {
  251. return (ctx->major);
  252. }
  253. /* We can't copy this structure, so we just move the pointer to it */
  254. client->creds = ctx->client_creds;
  255. ctx->client_creds = GSS_C_NO_CREDENTIAL;
  256. return (ctx->major);
  257. }
  258. /* As user - called on fatal/exit */
  259. void
  260. ssh_gssapi_cleanup_creds(void)
  261. {
  262. if (gssapi_client.store.filename != NULL) {
  263. /* Unlink probably isn't sufficient */
  264. debug("removing gssapi cred file\"%s\"",
  265. gssapi_client.store.filename);
  266. unlink(gssapi_client.store.filename);
  267. }
  268. }
  269. void
  270. ssh_gssapi_free_store(void)
  271. {
  272. if (gssapi_client.store.filename != NULL) {
  273. xfree(gssapi_client.store.filename);
  274. gssapi_client.store.filename = NULL;
  275. }
  276. if (gssapi_client.store.envval != NULL) {
  277. xfree(gssapi_client.store.envval);
  278. gssapi_client.store.envval = NULL;
  279. }
  280. }
  281. /* As user */
  282. void
  283. ssh_gssapi_storecreds(void)
  284. {
  285. if (gssapi_client.mech && gssapi_client.mech->storecreds) {
  286. (*gssapi_client.mech->storecreds)(&gssapi_client);
  287. } else
  288. debug("ssh_gssapi_storecreds: Not a GSSAPI mechanism");
  289. }
  290. /* This allows GSSAPI methods to do things to the child's environment based
  291. * on the passed authentication process and credentials.
  292. */
  293. /* As user */
  294. void
  295. ssh_gssapi_do_child(char ***envp, u_int *envsizep)
  296. {
  297. if (gssapi_client.store.envvar != NULL &&
  298. gssapi_client.store.envval != NULL) {
  299. debug("Setting %s to %s", gssapi_client.store.envvar,
  300. gssapi_client.store.envval);
  301. child_set_env(envp, envsizep, gssapi_client.store.envvar,
  302. gssapi_client.store.envval);
  303. }
  304. }
  305. /* Privileged */
  306. int
  307. ssh_gssapi_userok(char *user)
  308. {
  309. OM_uint32 lmin;
  310. if (gssapi_client.exportedname.length == 0 ||
  311. gssapi_client.exportedname.value == NULL) {
  312. debug("No suitable client data");
  313. return 0;
  314. }
  315. if (gssapi_client.mech && gssapi_client.mech->userok)
  316. if ((*gssapi_client.mech->userok)(&gssapi_client, user))
  317. return 1;
  318. else {
  319. /* Destroy delegated credentials if userok fails */
  320. gss_release_buffer(&lmin, &gssapi_client.displayname);
  321. gss_release_buffer(&lmin, &gssapi_client.exportedname);
  322. gss_release_cred(&lmin, &gssapi_client.creds);
  323. explicit_bzero(&gssapi_client,
  324. sizeof(ssh_gssapi_client));
  325. return 0;
  326. }
  327. else
  328. debug("ssh_gssapi_userok: Unknown GSSAPI mechanism");
  329. return (0);
  330. }
  331. /* Privileged */
  332. OM_uint32
  333. ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
  334. {
  335. ctx->major = gss_verify_mic(&ctx->minor, ctx->context,
  336. gssbuf, gssmic, NULL);
  337. return (ctx->major);
  338. }
  339. /* Privileged */
  340. const char *ssh_gssapi_displayname(void)
  341. {
  342. if (gssapi_client.displayname.length == 0 ||
  343. gssapi_client.displayname.value == NULL)
  344. return NULL;
  345. return (char *)gssapi_client.displayname.value;
  346. }
  347. #endif