gssapi.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. /*
  2. * OpenConnect (SSL + DTLS) VPN client
  3. *
  4. * Copyright © 2008-2015 Intel Corporation.
  5. *
  6. * Author: David Woodhouse <dwmw2@infradead.org>
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU Lesser General Public License
  10. * version 2.1, as published by the Free Software Foundation.
  11. *
  12. * This program is distributed in the hope that it will be useful, but
  13. * WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. */
  17. #include <config.h>
  18. #include "openconnect-internal.h"
  19. #include <errno.h>
  20. #include <string.h>
  21. static void print_gss_err(struct openconnect_info *vpninfo, const char *where,
  22. gss_OID mech, OM_uint32 err_maj, OM_uint32 err_min)
  23. {
  24. OM_uint32 major, minor, msg_ctx = 0;
  25. gss_buffer_desc status;
  26. do {
  27. major = gss_display_status(&minor, err_maj, GSS_C_GSS_CODE,
  28. mech, &msg_ctx, &status);
  29. if (GSS_ERROR(major))
  30. break;
  31. vpn_progress(vpninfo, PRG_ERR, "%s: %s\n", where, (char *)status.value);
  32. gss_release_buffer(&minor, &status);
  33. } while (msg_ctx);
  34. msg_ctx = 0;
  35. do {
  36. major = gss_display_status(&minor, err_min, GSS_C_MECH_CODE,
  37. mech, &msg_ctx, &status);
  38. if (GSS_ERROR(major))
  39. break;
  40. vpn_progress(vpninfo, PRG_ERR, "%s: %s\n", where, (char *)status.value);
  41. gss_release_buffer(&minor, &status);
  42. } while (msg_ctx);
  43. }
  44. static const char spnego_OID[] = "\x2b\x06\x01\x05\x05\x02";
  45. static const gss_OID_desc gss_mech_spnego = {
  46. 6,
  47. (void *)&spnego_OID
  48. };
  49. static int gssapi_setup(struct openconnect_info *vpninfo, struct http_auth_state *auth_state,
  50. const char *service, int proxy)
  51. {
  52. OM_uint32 major, minor;
  53. gss_buffer_desc token = GSS_C_EMPTY_BUFFER;
  54. char *name;
  55. if (asprintf(&name, "%s@%s", service,
  56. proxy ? vpninfo->proxy : vpninfo->hostname) == -1)
  57. return -ENOMEM;
  58. token.length = strlen(name);
  59. token.value = name;
  60. major = gss_import_name(&minor, &token, (gss_OID)GSS_C_NT_HOSTBASED_SERVICE,
  61. &auth_state->gss_target_name);
  62. free(name);
  63. if (GSS_ERROR(major)) {
  64. vpn_progress(vpninfo, PRG_ERR,
  65. _("Error importing GSSAPI name for authentication:\n"));
  66. print_gss_err(vpninfo, "gss_import_name()", GSS_C_NO_OID, major, minor);
  67. return -EIO;
  68. }
  69. return 0;
  70. }
  71. #define GSSAPI_CONTINUE 2
  72. #define GSSAPI_COMPLETE 3
  73. int gssapi_authorization(struct openconnect_info *vpninfo, int proxy,
  74. struct http_auth_state *auth_state,
  75. struct oc_text_buf *hdrbuf)
  76. {
  77. OM_uint32 major, minor;
  78. gss_buffer_desc in = GSS_C_EMPTY_BUFFER;
  79. gss_buffer_desc out = GSS_C_EMPTY_BUFFER;
  80. gss_OID mech = GSS_C_NO_OID;
  81. if (auth_state->state == AUTH_AVAILABLE && gssapi_setup(vpninfo, auth_state, "HTTP", proxy)) {
  82. auth_state->state = AUTH_FAILED;
  83. return -EIO;
  84. }
  85. if (auth_state->challenge && *auth_state->challenge) {
  86. int len = -EINVAL;
  87. in.value = openconnect_base64_decode(&len, auth_state->challenge);
  88. if (!in.value)
  89. return len;
  90. in.length = len;
  91. } else if (auth_state->state > AUTH_AVAILABLE) {
  92. /* This indicates failure. We were trying, but got an empty
  93. 'Proxy-Authorization: Negotiate' header back from the server
  94. implying that we should start again... */
  95. goto fail_gssapi;
  96. }
  97. major = gss_init_sec_context(&minor, GSS_C_NO_CREDENTIAL,
  98. &auth_state->gss_context,
  99. auth_state->gss_target_name,
  100. (gss_OID)&gss_mech_spnego,
  101. GSS_C_MUTUAL_FLAG, GSS_C_INDEFINITE,
  102. GSS_C_NO_CHANNEL_BINDINGS, &in,
  103. &mech, &out, NULL, NULL);
  104. if (in.value)
  105. free(in.value);
  106. if (major == GSS_S_COMPLETE)
  107. auth_state->state = GSSAPI_COMPLETE;
  108. else if (major == GSS_S_CONTINUE_NEEDED)
  109. auth_state->state = GSSAPI_CONTINUE;
  110. else {
  111. vpn_progress(vpninfo, PRG_ERR,
  112. _("Error generating GSSAPI response:\n"));
  113. print_gss_err(vpninfo, "gss_init_sec_context()", mech, major, minor);
  114. fail_gssapi:
  115. auth_state->state = AUTH_FAILED;
  116. cleanup_gssapi_auth(vpninfo, auth_state);
  117. /* If we were *trying*, then -EAGAIN. Else -ENOENT to let another
  118. auth method try without having to reconnect first. */
  119. return in.value ? -EAGAIN : -ENOENT;
  120. }
  121. buf_append(hdrbuf, "%sAuthorization: Negotiate ", proxy ? "Proxy-" : "");
  122. buf_append_base64(hdrbuf, out.value, out.length, 0);
  123. buf_append(hdrbuf, "\r\n");
  124. gss_release_buffer(&minor, &out);
  125. if (!auth_state->challenge) {
  126. if (proxy)
  127. vpn_progress(vpninfo, PRG_INFO,
  128. _("Attempting GSSAPI authentication to proxy\n"));
  129. else
  130. vpn_progress(vpninfo, PRG_INFO,
  131. _("Attempting GSSAPI authentication to server '%s'\n"),
  132. vpninfo->hostname);
  133. }
  134. return 0;
  135. }
  136. /* auth_state is NULL when called from socks_gssapi_auth() */
  137. void cleanup_gssapi_auth(struct openconnect_info *vpninfo,
  138. struct http_auth_state *auth_state)
  139. {
  140. OM_uint32 minor;
  141. if (!auth_state)
  142. return;
  143. if (auth_state->gss_target_name != GSS_C_NO_NAME)
  144. gss_release_name(&minor, &auth_state->gss_target_name);
  145. if (auth_state->gss_context != GSS_C_NO_CONTEXT)
  146. gss_delete_sec_context(&minor, &auth_state->gss_context, GSS_C_NO_BUFFER);
  147. /* Shouldn't be necessary, but make sure... */
  148. auth_state->gss_target_name = GSS_C_NO_NAME;
  149. auth_state->gss_context = GSS_C_NO_CONTEXT;
  150. }
  151. int socks_gssapi_auth(struct openconnect_info *vpninfo)
  152. {
  153. gss_buffer_desc in = GSS_C_EMPTY_BUFFER;
  154. gss_buffer_desc out = GSS_C_EMPTY_BUFFER;
  155. gss_OID mech = GSS_C_NO_OID;
  156. OM_uint32 major, minor;
  157. unsigned char *pktbuf;
  158. int i;
  159. int ret = -EIO;
  160. struct http_auth_state *auth_state = &vpninfo->proxy_auth[AUTH_TYPE_GSSAPI];
  161. if (gssapi_setup(vpninfo, auth_state, "rcmd", 1))
  162. return -EIO;
  163. pktbuf = malloc(65538);
  164. if (!pktbuf)
  165. return -ENOMEM;
  166. while (1) {
  167. major = gss_init_sec_context(&minor, GSS_C_NO_CREDENTIAL, &auth_state->gss_context,
  168. auth_state->gss_target_name, (gss_OID)&gss_mech_spnego,
  169. GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_DELEG_FLAG | GSS_C_SEQUENCE_FLAG,
  170. GSS_C_INDEFINITE, GSS_C_NO_CHANNEL_BINDINGS, &in, &mech,
  171. &out, NULL, NULL);
  172. in.value = NULL;
  173. if (major == GSS_S_COMPLETE) {
  174. /* If we still have a token to send, send it. */
  175. if (!out.length) {
  176. vpn_progress(vpninfo, PRG_DEBUG,
  177. _("GSSAPI authentication completed\n"));
  178. gss_release_buffer(&minor, &out);
  179. ret = 0;
  180. break;
  181. }
  182. } else if (major != GSS_S_CONTINUE_NEEDED) {
  183. print_gss_err(vpninfo, "gss_init_sec_context()", mech, major, minor);
  184. break;
  185. }
  186. if (out.length > 65535) {
  187. vpn_progress(vpninfo, PRG_ERR,
  188. _("GSSAPI token too large (%zd bytes)\n"),
  189. out.length);
  190. break;
  191. }
  192. pktbuf[0] = 1; /* ver */
  193. pktbuf[1] = 1; /* mtyp */
  194. store_be16(pktbuf + 2, out.length);
  195. memcpy(pktbuf + 4, out.value, out.length);
  196. free(out.value);
  197. vpn_progress(vpninfo, PRG_TRACE,
  198. _("Sending GSSAPI token of %zu bytes\n"), out.length + 4);
  199. i = vpninfo->ssl_write(vpninfo, (void *)pktbuf, out.length + 4);
  200. if (i < 0) {
  201. vpn_progress(vpninfo, PRG_ERR,
  202. _("Failed to send GSSAPI authentication token to proxy: %s\n"),
  203. strerror(-i));
  204. break;
  205. }
  206. i = vpninfo->ssl_read(vpninfo, (void *)pktbuf, 4);
  207. if (i < 0) {
  208. vpn_progress(vpninfo, PRG_ERR,
  209. _("Failed to receive GSSAPI authentication token from proxy: %s\n"),
  210. strerror(-i));
  211. break;
  212. }
  213. if (pktbuf[1] == 0xff) {
  214. vpn_progress(vpninfo, PRG_ERR,
  215. _("SOCKS server reported GSSAPI context failure\n"));
  216. break;
  217. } else if (pktbuf[1] != 1) {
  218. vpn_progress(vpninfo, PRG_ERR,
  219. _("Unknown GSSAPI status response (0x%02x) from SOCKS server\n"),
  220. pktbuf[1]);
  221. break;
  222. }
  223. in.length = load_be16(pktbuf + 2);
  224. in.value = pktbuf;
  225. if (!in.length) {
  226. vpn_progress(vpninfo, PRG_DEBUG,
  227. _("GSSAPI authentication completed\n"));
  228. ret = 0;
  229. break;
  230. }
  231. i = vpninfo->ssl_read(vpninfo, (void *)pktbuf, in.length);
  232. if (i < 0) {
  233. vpn_progress(vpninfo, PRG_ERR,
  234. _("Failed to receive GSSAPI authentication token from proxy: %s\n"),
  235. strerror(-i));
  236. break;
  237. }
  238. vpn_progress(vpninfo, PRG_TRACE, _("Got GSSAPI token of %zu bytes: %02x %02x %02x %02x\n"),
  239. in.length, pktbuf[0], pktbuf[1], pktbuf[2], pktbuf[3]);
  240. }
  241. if (!ret) {
  242. ret = -EIO;
  243. pktbuf[0] = 0;
  244. in.value = pktbuf;
  245. in.length = 1;
  246. major = gss_wrap(&minor, auth_state->gss_context, 0,
  247. GSS_C_QOP_DEFAULT, &in, NULL, &out);
  248. if (major != GSS_S_COMPLETE) {
  249. print_gss_err(vpninfo, "gss_wrap()", mech, major, minor);
  250. goto err;
  251. }
  252. pktbuf[0] = 1;
  253. pktbuf[1] = 2;
  254. store_be16(pktbuf + 2, out.length);
  255. memcpy(pktbuf + 4, out.value, out.length);
  256. free(out.value);
  257. vpn_progress(vpninfo, PRG_TRACE,
  258. _("Sending GSSAPI protection negotiation of %zu bytes\n"), out.length + 4);
  259. i = vpninfo->ssl_write(vpninfo, (void *)pktbuf, out.length + 4);
  260. if (i < 0) {
  261. vpn_progress(vpninfo, PRG_ERR,
  262. _("Failed to send GSSAPI protection response to proxy: %s\n"),
  263. strerror(-i));
  264. goto err;
  265. }
  266. i = vpninfo->ssl_read(vpninfo, (void *)pktbuf, 4);
  267. if (i < 0) {
  268. vpn_progress(vpninfo, PRG_ERR,
  269. _("Failed to receive GSSAPI protection response from proxy: %s\n"),
  270. strerror(-i));
  271. goto err;
  272. }
  273. in.length = load_be16(pktbuf + 2);
  274. in.value = pktbuf;
  275. i = vpninfo->ssl_read(vpninfo, (void *)pktbuf, in.length);
  276. if (i < 0) {
  277. vpn_progress(vpninfo, PRG_ERR,
  278. _("Failed to receive GSSAPI protection response from proxy: %s\n"),
  279. strerror(-i));
  280. goto err;
  281. }
  282. vpn_progress(vpninfo, PRG_TRACE,
  283. _("Got GSSAPI protection response of %zu bytes: %02x %02x %02x %02x\n"),
  284. in.length, pktbuf[0], pktbuf[1], pktbuf[2], pktbuf[3]);
  285. major = gss_unwrap(&minor, auth_state->gss_context, &in, &out, NULL, GSS_C_QOP_DEFAULT);
  286. if (major != GSS_S_COMPLETE) {
  287. print_gss_err(vpninfo, "gss_unwrap()", mech, major, minor);
  288. goto err;
  289. }
  290. if (out.length != 1) {
  291. vpn_progress(vpninfo, PRG_ERR,
  292. _("Invalid GSSAPI protection response from proxy (%zu bytes)\n"),
  293. out.length);
  294. gss_release_buffer(&minor, &out);
  295. goto err;
  296. }
  297. i = *(char *)out.value;
  298. gss_release_buffer(&minor, &out);
  299. if (i == 1) {
  300. vpn_progress(vpninfo, PRG_ERR,
  301. _("SOCKS proxy demands message integrity, which is not supported\n"));
  302. goto err;
  303. } else if (i == 2) {
  304. vpn_progress(vpninfo, PRG_ERR,
  305. _("SOCKS proxy demands message confidentiality, which is not supported\n"));
  306. goto err;
  307. } else if (i) {
  308. vpn_progress(vpninfo, PRG_ERR,
  309. _("SOCKS proxy demands protection unknown type 0x%02x\n"),
  310. (unsigned char)i);
  311. goto err;
  312. }
  313. ret = 0;
  314. }
  315. err:
  316. cleanup_gssapi_auth(vpninfo, NULL);
  317. free(pktbuf);
  318. return ret;
  319. }