http-auth.c 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  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 <unistd.h>
  20. #include <fcntl.h>
  21. #include <time.h>
  22. #include <string.h>
  23. #include <ctype.h>
  24. #include <errno.h>
  25. #include <stdlib.h>
  26. #include <stdio.h>
  27. #include <stdarg.h>
  28. static int basic_authorization(struct openconnect_info *vpninfo, int proxy,
  29. struct http_auth_state *auth_state,
  30. struct oc_text_buf *hdrbuf)
  31. {
  32. struct oc_text_buf *text;
  33. const char *user, *pass;
  34. if (proxy) {
  35. user = vpninfo->proxy_user;
  36. pass = vpninfo->proxy_pass;
  37. } else {
  38. /* Need to parse this out of the URL */
  39. return -EINVAL;
  40. }
  41. if (!user || !pass)
  42. return -EINVAL;
  43. if (auth_state->state == AUTH_IN_PROGRESS) {
  44. auth_state->state = AUTH_FAILED;
  45. return -EAGAIN;
  46. }
  47. text = buf_alloc();
  48. buf_append(text, "%s:%s", user, pass);
  49. if (buf_error(text))
  50. return buf_free(text);
  51. buf_append(hdrbuf, "%sAuthorization: Basic ", proxy ? "Proxy-" : "");
  52. buf_append_base64(hdrbuf, text->data, text->pos, 0);
  53. buf_append(hdrbuf, "\r\n");
  54. memset(text->data, 0, text->pos);
  55. buf_free(text);
  56. if (proxy)
  57. vpn_progress(vpninfo, PRG_INFO, _("Attempting HTTP Basic authentication to proxy\n"));
  58. else
  59. vpn_progress(vpninfo, PRG_INFO, _("Attempting HTTP Basic authentication to server '%s'\n"),
  60. vpninfo->hostname);
  61. auth_state->state = AUTH_IN_PROGRESS;
  62. return 0;
  63. }
  64. static int bearer_authorization(struct openconnect_info *vpninfo, int proxy,
  65. struct http_auth_state *auth_state,
  66. struct oc_text_buf *hdrbuf)
  67. {
  68. const char *bearer_token = vpninfo->bearer_token;
  69. if (proxy) {
  70. return -EINVAL;
  71. }
  72. if (!bearer_token)
  73. return -EINVAL;
  74. if (auth_state->state == AUTH_IN_PROGRESS) {
  75. auth_state->state = AUTH_FAILED;
  76. return -EAGAIN;
  77. }
  78. buf_append(hdrbuf, "Authorization: Bearer %s\r\n", bearer_token);
  79. vpn_progress(vpninfo, PRG_INFO, _("Attempting HTTP Bearer authentication to server '%s'\n"),
  80. vpninfo->hostname);
  81. auth_state->state = AUTH_IN_PROGRESS;
  82. return 0;
  83. }
  84. #if !defined(HAVE_GSSAPI) && !defined(_WIN32)
  85. static int no_gssapi_authorization(struct openconnect_info *vpninfo, int proxy,
  86. struct http_auth_state *auth_state,
  87. struct oc_text_buf *hdrbuf)
  88. {
  89. /* This comes last so just complain. We're about to bail. */
  90. vpn_progress(vpninfo, PRG_ERR,
  91. _("This version of OpenConnect was built without GSSAPI support\n"));
  92. auth_state->state = AUTH_FAILED;
  93. return -ENOENT;
  94. }
  95. #endif
  96. struct auth_method {
  97. int state_index;
  98. const char *name;
  99. int (*authorization)(struct openconnect_info *, int, struct http_auth_state *, struct oc_text_buf *);
  100. void (*cleanup)(struct openconnect_info *, struct http_auth_state *);
  101. } auth_methods[] = {
  102. #if defined(HAVE_GSSAPI) || defined(_WIN32)
  103. { AUTH_TYPE_GSSAPI, "Negotiate", gssapi_authorization, cleanup_gssapi_auth },
  104. #endif
  105. { AUTH_TYPE_NTLM, "NTLM", ntlm_authorization, cleanup_ntlm_auth },
  106. { AUTH_TYPE_DIGEST, "Digest", digest_authorization, NULL },
  107. { AUTH_TYPE_BASIC, "Basic", basic_authorization, NULL },
  108. { AUTH_TYPE_BEARER, "Bearer", bearer_authorization, NULL },
  109. #if !defined(HAVE_GSSAPI) && !defined(_WIN32)
  110. { AUTH_TYPE_GSSAPI, "Negotiate", no_gssapi_authorization, NULL }
  111. #endif
  112. };
  113. /* Generate Proxy-Authorization: header for request if appropriate */
  114. int gen_authorization_hdr(struct openconnect_info *vpninfo, int proxy,
  115. struct oc_text_buf *buf)
  116. {
  117. int ret;
  118. int i;
  119. for (i = 0; i < ARRAY_SIZE(auth_methods); i++) {
  120. struct http_auth_state *auth_state;
  121. if (proxy)
  122. auth_state = &vpninfo->proxy_auth[auth_methods[i].state_index];
  123. else
  124. auth_state = &vpninfo->http_auth[auth_methods[i].state_index];
  125. if (auth_state->state == AUTH_DEFAULT_DISABLED) {
  126. if (proxy)
  127. vpn_progress(vpninfo, PRG_ERR,
  128. _("Proxy requested Basic authentication which is disabled by default\n"));
  129. else
  130. vpn_progress(vpninfo, PRG_ERR,
  131. _("Server '%s' requested Basic authentication which is disabled by default\n"),
  132. vpninfo->hostname);
  133. auth_state->state = AUTH_FAILED;
  134. return -EINVAL;
  135. }
  136. if (auth_state->state > AUTH_UNSEEN) {
  137. ret = auth_methods[i].authorization(vpninfo, proxy, auth_state, buf);
  138. if (ret == -EAGAIN || !ret)
  139. return ret;
  140. }
  141. }
  142. vpn_progress(vpninfo, PRG_INFO, _("No more authentication methods to try\n"));
  143. if (vpninfo->retry_on_auth_fail) {
  144. /* Try again without the X-Support-HTTP-Auth: header */
  145. vpninfo->try_http_auth = 0;
  146. return 0;
  147. }
  148. return -ENOENT;
  149. }
  150. /* Returns non-zero if it matched */
  151. static int handle_auth_proto(struct openconnect_info *vpninfo,
  152. struct http_auth_state *auth_states,
  153. struct auth_method *method, char *hdr)
  154. {
  155. struct http_auth_state *auth = &auth_states[method->state_index];
  156. int l = strlen(method->name);
  157. if (auth->state <= AUTH_FAILED)
  158. return 0;
  159. if (strncmp(method->name, hdr, l))
  160. return 0;
  161. if (hdr[l] != ' ' && hdr[l] != 0)
  162. return 0;
  163. if (auth->state == AUTH_UNSEEN)
  164. auth->state = AUTH_AVAILABLE;
  165. free(auth->challenge);
  166. if (hdr[l])
  167. auth->challenge = strdup(hdr + l + 1);
  168. else
  169. auth->challenge = NULL;
  170. return 1;
  171. }
  172. int proxy_auth_hdrs(struct openconnect_info *vpninfo, char *hdr, char *val)
  173. {
  174. int i;
  175. if (!strcasecmp(hdr, "Proxy-Connection") ||
  176. !strcasecmp(hdr, "Connection")) {
  177. if (!strcasecmp(val, "close"))
  178. vpninfo->proxy_close_during_auth = 1;
  179. return 0;
  180. }
  181. if (strcasecmp(hdr, "Proxy-Authenticate"))
  182. return 0;
  183. for (i = 0; i < ARRAY_SIZE(auth_methods); i++) {
  184. /* Return once we've found a match */
  185. if (handle_auth_proto(vpninfo, vpninfo->proxy_auth, &auth_methods[i], val))
  186. return 0;
  187. }
  188. return 0;
  189. }
  190. int http_auth_hdrs(struct openconnect_info *vpninfo, char *hdr, char *val)
  191. {
  192. int i;
  193. if (!strcasecmp(hdr, "X-HTTP-Auth-Support") &&
  194. !strcasecmp(val, "fallback")) {
  195. vpninfo->retry_on_auth_fail = 1;
  196. return 0;
  197. }
  198. if (strcasecmp(hdr, "WWW-Authenticate"))
  199. return 0;
  200. for (i = 0; i < ARRAY_SIZE(auth_methods); i++) {
  201. /* Return once we've found a match */
  202. if (handle_auth_proto(vpninfo, vpninfo->http_auth, &auth_methods[i], val))
  203. return 0;
  204. }
  205. return 0;
  206. }
  207. void clear_auth_states(struct openconnect_info *vpninfo,
  208. struct http_auth_state *auth_states, int reset)
  209. {
  210. int i;
  211. for (i = 0; i < ARRAY_SIZE(auth_methods); i++) {
  212. struct http_auth_state *auth = &auth_states[auth_methods[i].state_index];
  213. /* The 'reset' argument is set when we're connected successfully,
  214. to fully reset the state to allow another connection to start
  215. again. Otherwise, we need to remember which auth methods have
  216. been tried and should not be attempted again. */
  217. if (reset && auth_methods[i].cleanup)
  218. auth_methods[i].cleanup(vpninfo, auth);
  219. free(auth->challenge);
  220. auth->challenge = NULL;
  221. /* If it *failed* don't try it again even next time */
  222. if (auth->state <= AUTH_FAILED)
  223. continue;
  224. if (reset || auth->state == AUTH_AVAILABLE)
  225. auth->state = AUTH_UNSEEN;
  226. }
  227. }
  228. static int set_authmethods(struct openconnect_info *vpninfo, struct http_auth_state *auth_states,
  229. const char *methods)
  230. {
  231. int i, len;
  232. const char *p;
  233. for (i = 0; i < ARRAY_SIZE(auth_methods); i++)
  234. auth_states[auth_methods[i].state_index].state = AUTH_DISABLED;
  235. while (methods) {
  236. p = strchr(methods, ',');
  237. if (p) {
  238. len = p - methods;
  239. p++;
  240. } else
  241. len = strlen(methods);
  242. for (i = 0; i < ARRAY_SIZE(auth_methods); i++) {
  243. if (strprefix_match(methods, len, auth_methods[i].name) ||
  244. (auth_methods[i].state_index == AUTH_TYPE_GSSAPI &&
  245. strprefix_match(methods, len, "gssapi"))) {
  246. auth_states[auth_methods[i].state_index].state = AUTH_UNSEEN;
  247. break;
  248. }
  249. }
  250. methods = p;
  251. }
  252. return 0;
  253. }
  254. int openconnect_set_http_auth(struct openconnect_info *vpninfo, const char *methods)
  255. {
  256. return set_authmethods(vpninfo, vpninfo->http_auth, methods);
  257. }
  258. int openconnect_set_proxy_auth(struct openconnect_info *vpninfo, const char *methods)
  259. {
  260. return set_authmethods(vpninfo, vpninfo->proxy_auth, methods);
  261. }