2
0

auth_plugin_ntlm.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  1. /*
  2. * This file is part of the Sofia-SIP package
  3. *
  4. * Copyright (C) 2005 Nokia Corporation.
  5. *
  6. * Contact: Pekka Pessi <pekka.pessi@nokia.com>
  7. *
  8. * This library is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU Lesser General Public License
  10. * as published by the Free Software Foundation; either version 2.1 of
  11. * the License, or (at your option) any later version.
  12. *
  13. * This library is distributed in the hope that it will be useful, but
  14. * WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. * Lesser General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU Lesser General Public
  19. * License along with this library; if not, write to the Free Software
  20. * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  21. * 02110-1301 USA
  22. *
  23. */
  24. /**@CFILE auth_plugin_ntlm.c
  25. *
  26. * @brief Plugin for delayed authentication.
  27. *
  28. * This authentication plugin provides authentication operation that is
  29. * intentionally delayed. It serves as an example of server-side
  30. * authentication plugins.
  31. *
  32. * @author Pekka Pessi <Pekka.Pessi@nokia.com>.
  33. *
  34. * @date Created: Wed Apr 11 15:14:03 2001 ppessi
  35. */
  36. #include "config.h"
  37. #include <stddef.h>
  38. #include <stdlib.h>
  39. #include <string.h>
  40. #include <stdio.h>
  41. #include <assert.h>
  42. #include <sofia-sip/su_debug.h>
  43. #include <sofia-sip/su_wait.h>
  44. #include <sofia-sip/su_alloc.h>
  45. #include <sofia-sip/su_tagarg.h>
  46. #include "sofia-sip/auth_module.h"
  47. #include "sofia-sip/auth_plugin.h"
  48. #include "sofia-sip/auth_ntlm.h"
  49. #if HAVE_FUNC
  50. #elif HAVE_FUNCTION
  51. #define __func__ __FUNCTION__
  52. #else
  53. static char const __func__[] = "auth_plugin_ntml";
  54. #endif
  55. /* ====================================================================== */
  56. /* NTLM authentication scheme */
  57. static int auth_init_ntlm(auth_mod_t *am,
  58. auth_scheme_t *base,
  59. su_root_t *root,
  60. tag_type_t tag, tag_value_t value, ...);
  61. static void auth_method_ntlm_x(auth_mod_t *am,
  62. auth_status_t *as,
  63. msg_auth_t *au,
  64. auth_challenger_t const *ach);
  65. auth_scheme_t auth_scheme_ntlm[1] =
  66. {{
  67. "NTLM", /* asch_method */
  68. sizeof (auth_mod_t), /* asch_size */
  69. auth_init_default, /* asch_init */
  70. auth_method_ntlm_x, /* asch_check */
  71. auth_challenge_ntlm, /* asch_challenge */
  72. auth_cancel_default, /* asch_cancel */
  73. auth_destroy_default /* asch_destroy */
  74. }};
  75. #define AUTH_NTLM_NONCE_LEN (BASE64_SIZE(sizeof (struct nonce)) + 1)
  76. static int auth_init_ntlm(auth_mod_t *am,
  77. auth_scheme_t *base,
  78. su_root_t *root,
  79. tag_type_t tag, tag_value_t value, ...)
  80. {
  81. auth_plugin_t *ap = AUTH_PLUGIN(am);
  82. int retval = -1;
  83. ta_list ta;
  84. ta_start(ta, tag, value);
  85. if (auth_init_default(am, NULL, root, ta_tags(ta)) != -1) {
  86. retval = 0;
  87. }
  88. ta_end(ta);
  89. return retval;
  90. }
  91. /** Authenticate a request with @b NTLM authentication scheme.
  92. *
  93. * This function reads user database before authentication, if needed.
  94. */
  95. static
  96. void auth_method_ntlm_x(auth_mod_t *am,
  97. auth_status_t *as,
  98. msg_auth_t *au,
  99. auth_challenger_t const *ach)
  100. {
  101. if (am) {
  102. auth_readdb_if_needed(am);
  103. auth_method_ntlm(am, as, au, ach);
  104. }
  105. }
  106. /** Authenticate a request with @b Ntlm authentication scheme.
  107. */
  108. void auth_method_ntlm(auth_mod_t *am,
  109. auth_status_t *as,
  110. msg_auth_t *au,
  111. auth_challenger_t const *ach)
  112. {
  113. as->as_allow = as->as_allow || auth_allow_check(am, as) == 0;
  114. if (as->as_realm)
  115. au = auth_ntlm_credentials(au, as->as_realm, am->am_opaque,
  116. am->am_gssapi_data, am->am_targetname);
  117. else
  118. au = NULL;
  119. if (as->as_allow) {
  120. SU_DEBUG_5(("%s: allow unauthenticated %s\n", __func__, as->as_method));
  121. as->as_status = 0, as->as_phrase = NULL;
  122. as->as_match = (msg_header_t *)au;
  123. return;
  124. }
  125. if (au) {
  126. auth_response_t ar[1] = {{ sizeof(ar) }};
  127. auth_ntlm_response_get(as->as_home, ar, au->au_params);
  128. as->as_match = (msg_header_t *)au;
  129. auth_check_ntlm(am, as, ar, ach);
  130. }
  131. else {
  132. /* There was no matching credentials, send challenge */
  133. SU_DEBUG_5(("%s: no credentials matched\n", __func__));
  134. auth_challenge_ntlm(am, as, ach);
  135. }
  136. }
  137. /** Find a NTLM credential header with matching realm and opaque. */
  138. msg_auth_t *auth_ntlm_credentials(msg_auth_t *auth,
  139. char const *realm,
  140. char const *opaque,
  141. char const *gssapidata,
  142. char const *targetname)
  143. {
  144. char const *agssapidata, *atargetname;
  145. for (;auth; auth = auth_mod_credentials(auth->au_next)) {
  146. if (!su_casematch(auth->au_scheme, "NTLM"))
  147. continue;
  148. if (gssapidata) {
  149. agssapidata = msg_header_find_param(auth->au_common, "gssapi-data=");
  150. if (!agssapidata || auth_strcmp(agssapidata, gssapidata))
  151. continue;
  152. }
  153. if (targetname) {
  154. atargetname = msg_header_find_param(auth->au_common, "targetname=");
  155. if (!atargetname || auth_strcmp(atargetname, targetname))
  156. continue;
  157. }
  158. return auth;
  159. }
  160. return NULL;
  161. }
  162. /** Check ntlm authentication */
  163. void auth_check_ntlm(auth_mod_t *am,
  164. auth_status_t *as,
  165. auth_response_t *ar,
  166. auth_challenger_t const *ach)
  167. {
  168. char const *a1;
  169. auth_hexmd5_t a1buf, response;
  170. auth_passwd_t *apw;
  171. char const *phrase;
  172. msg_time_t now = msg_now();
  173. if (am == NULL || as == NULL || ar == NULL || ach == NULL) {
  174. if (as) {
  175. as->as_status = 500, as->as_phrase = "Internal Server Error";
  176. as->as_response = NULL;
  177. }
  178. return;
  179. }
  180. phrase = "Bad authorization";
  181. #define PA "Authorization missing "
  182. if ((!ar->ar_username && (phrase = PA "username")) ||
  183. (!ar->ar_nonce && (phrase = PA "nonce")) ||
  184. (!ar->ar_uri && (phrase = PA "URI")) ||
  185. (!ar->ar_response && (phrase = PA "response")) ||
  186. /* (!ar->ar_opaque && (phrase = PA "opaque")) || */
  187. /* Check for qop */
  188. (ar->ar_qop &&
  189. ((ar->ar_auth &&
  190. !su_casematch(ar->ar_qop, "auth") &&
  191. !su_casematch(ar->ar_qop, "\"auth\"")) ||
  192. (ar->ar_auth_int &&
  193. !su_casematch(ar->ar_qop, "auth-int") &&
  194. !su_casematch(ar->ar_qop, "\"auth-int\"")))
  195. && (phrase = PA "has invalid qop"))) {
  196. assert(phrase);
  197. SU_DEBUG_5(("auth_method_ntlm: 400 %s\n", phrase));
  198. as->as_status = 400, as->as_phrase = phrase;
  199. as->as_response = NULL;
  200. return;
  201. }
  202. /* XXX - replace */
  203. #if 0
  204. if (as->as_nonce_issued == 0 /* Already validated nonce */ &&
  205. auth_validate_ntlm_nonce(am, as, ar, now) < 0) {
  206. #else
  207. if (as->as_nonce_issued == 0 /* Already validated nonce */ &&
  208. auth_validate_digest_nonce(am, as, ar, now) < 0) {
  209. #endif
  210. as->as_blacklist = am->am_blacklist;
  211. auth_challenge_ntlm(am, as, ach);
  212. return;
  213. }
  214. if (as->as_stale) {
  215. auth_challenge_ntlm(am, as, ach);
  216. return;
  217. }
  218. apw = auth_mod_getpass(am, ar->ar_username, ar->ar_realm);
  219. #if 0
  220. if (apw && apw->apw_hash)
  221. a1 = apw->apw_hash;
  222. else if (apw && apw->apw_pass)
  223. auth_ntlm_a1(ar, a1buf, apw->apw_pass), a1 = a1buf;
  224. else
  225. auth_ntlm_a1(ar, a1buf, "xyzzy"), a1 = a1buf, apw = NULL;
  226. if (ar->ar_md5sess)
  227. auth_ntlm_a1sess(ar, a1buf, a1), a1 = a1buf;
  228. #else
  229. if (apw && apw->apw_hash)
  230. a1 = apw->apw_hash;
  231. else if (apw && apw->apw_pass)
  232. auth_digest_a1(ar, a1buf, apw->apw_pass), a1 = a1buf;
  233. else
  234. auth_digest_a1(ar, a1buf, "xyzzy"), a1 = a1buf, apw = NULL;
  235. if (ar->ar_md5sess)
  236. auth_digest_a1sess(ar, a1buf, a1), a1 = a1buf;
  237. #endif
  238. /* XXX - replace with auth_ntlm_response */
  239. #if 0
  240. auth_ntlm_response(ar, response, a1,
  241. as->as_method, as->as_body, as->as_bodylen);
  242. #else
  243. auth_digest_response(ar, response, a1,
  244. as->as_method, as->as_body, as->as_bodylen);
  245. #endif
  246. if (!apw || strcmp(response, ar->ar_response)) {
  247. if (am->am_forbidden) {
  248. as->as_status = 403, as->as_phrase = "Forbidden";
  249. as->as_blacklist = am->am_blacklist;
  250. as->as_response = NULL;
  251. }
  252. else {
  253. auth_challenge_ntlm(am, as, ach);
  254. as->as_blacklist = am->am_blacklist;
  255. }
  256. SU_DEBUG_5(("auth_method_ntlm: response did not match\n"));
  257. return;
  258. }
  259. assert(apw);
  260. as->as_user = apw->apw_user;
  261. as->as_anonymous = apw == am->am_anon_user;
  262. if (am->am_nextnonce || am->am_mutual)
  263. auth_info_ntlm(am, as, ach);
  264. if (am->am_challenge)
  265. auth_challenge_ntlm(am, as, ach);
  266. SU_DEBUG_7(("auth_method_ntlm: successful authentication\n"));
  267. as->as_status = 0; /* Successful authentication! */
  268. as->as_phrase = "";
  269. }
  270. /** Construct a challenge header for @b Ntlm authentication scheme. */
  271. void auth_challenge_ntlm(auth_mod_t *am,
  272. auth_status_t *as,
  273. auth_challenger_t const *ach)
  274. {
  275. char const *u, *d;
  276. char nonce[AUTH_NTLM_NONCE_LEN];
  277. #if 0
  278. auth_generate_ntlm_nonce(am, nonce, sizeof nonce, 0, msg_now());
  279. #else
  280. auth_generate_digest_nonce(am, nonce, sizeof nonce, 0, msg_now());
  281. #endif
  282. u = as->as_uri;
  283. d = as->as_pdomain;
  284. as->as_response =
  285. msg_header_format(as->as_home, ach->ach_header,
  286. "Ntlm"
  287. " realm=\"%s\","
  288. "%s%s%s"
  289. "%s%s%s"
  290. " nonce=\"%s\","
  291. "%s%s%s"
  292. "%s" /* stale */
  293. " algorithm=%s"
  294. "%s%s%s",
  295. as->as_realm,
  296. u ? " uri=\"" : "", u ? u : "", u ? "\"," : "",
  297. d ? " domain=\"" : "", d ? d : "", d ? "\"," : "",
  298. nonce,
  299. am->am_opaque ? " opaque=\"" : "",
  300. am->am_opaque ? am->am_opaque : "",
  301. am->am_opaque ? "\"," : "",
  302. as->as_stale ? " stale=true," : "",
  303. am->am_algorithm,
  304. am->am_qop ? ", qop=\"" : "",
  305. am->am_qop ? am->am_qop : "",
  306. am->am_qop ? "\"" : "");
  307. if (!as->as_response)
  308. as->as_status = 500, as->as_phrase = auth_internal_server_error;
  309. else
  310. as->as_status = ach->ach_status, as->as_phrase = ach->ach_phrase;
  311. }
  312. /** Construct a info header for @b Ntlm authentication scheme. */
  313. void auth_info_ntlm(auth_mod_t *am,
  314. auth_status_t *as,
  315. auth_challenger_t const *ach)
  316. {
  317. if (!ach->ach_info)
  318. return;
  319. if (am->am_nextnonce) {
  320. char nonce[AUTH_NTLM_NONCE_LEN];
  321. /* XXX - replace */
  322. #if 0
  323. auth_generate_ntlm_nonce(am, nonce, sizeof nonce, 1, msg_now());
  324. #else
  325. auth_generate_digest_nonce(am, nonce, sizeof nonce, 1, msg_now());
  326. #endif
  327. as->as_info =
  328. msg_header_format(as->as_home, ach->ach_info, "nextnonce=\"%s\"", nonce);
  329. }
  330. }