/crypto/heimdal/lib/gssapi/ntlm/init_sec_context.c

https://bitbucket.org/freebsd/freebsd-head/ · C · 501 lines · 384 code · 76 blank · 41 comment · 85 complexity · a49ab0500be4dfb9a1e06328b99222d3 MD5 · raw file

  1. /*
  2. * Copyright (c) 2006 - 2008 Kungliga Tekniska Högskolan
  3. * (Royal Institute of Technology, Stockholm, Sweden).
  4. * All rights reserved.
  5. *
  6. * Redistribution and use in source and binary forms, with or without
  7. * modification, are permitted provided that the following conditions
  8. * are met:
  9. *
  10. * 1. Redistributions of source code must retain the above copyright
  11. * notice, this list of conditions and the following disclaimer.
  12. *
  13. * 2. Redistributions in binary form must reproduce the above copyright
  14. * notice, this list of conditions and the following disclaimer in the
  15. * documentation and/or other materials provided with the distribution.
  16. *
  17. * 3. Neither the name of the Institute nor the names of its contributors
  18. * may be used to endorse or promote products derived from this software
  19. * without specific prior written permission.
  20. *
  21. * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
  22. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  23. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  24. * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
  25. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  26. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  27. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  28. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  29. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  30. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  31. * SUCH DAMAGE.
  32. */
  33. #include "ntlm.h"
  34. static int
  35. from_file(const char *fn, const char *target_domain,
  36. char **username, struct ntlm_buf *key)
  37. {
  38. char *str, buf[1024];
  39. FILE *f;
  40. f = fopen(fn, "r");
  41. if (f == NULL)
  42. return ENOENT;
  43. rk_cloexec_file(f);
  44. while (fgets(buf, sizeof(buf), f) != NULL) {
  45. char *d, *u, *p;
  46. buf[strcspn(buf, "\r\n")] = '\0';
  47. if (buf[0] == '#')
  48. continue;
  49. str = NULL;
  50. d = strtok_r(buf, ":", &str);
  51. if (d && strcasecmp(target_domain, d) != 0)
  52. continue;
  53. u = strtok_r(NULL, ":", &str);
  54. p = strtok_r(NULL, ":", &str);
  55. if (u == NULL || p == NULL)
  56. continue;
  57. *username = strdup(u);
  58. heim_ntlm_nt_key(p, key);
  59. memset(buf, 0, sizeof(buf));
  60. fclose(f);
  61. return 0;
  62. }
  63. memset(buf, 0, sizeof(buf));
  64. fclose(f);
  65. return ENOENT;
  66. }
  67. static int
  68. get_user_file(const ntlm_name target_name,
  69. char **username, struct ntlm_buf *key)
  70. {
  71. const char *fn;
  72. if (issuid())
  73. return ENOENT;
  74. fn = getenv("NTLM_USER_FILE");
  75. if (fn == NULL)
  76. return ENOENT;
  77. if (from_file(fn, target_name->domain, username, key) == 0)
  78. return 0;
  79. return ENOENT;
  80. }
  81. /*
  82. * Pick up the ntlm cred from the default krb5 credential cache.
  83. */
  84. static int
  85. get_user_ccache(const ntlm_name name, char **username, struct ntlm_buf *key)
  86. {
  87. krb5_context context = NULL;
  88. krb5_principal client;
  89. krb5_ccache id = NULL;
  90. krb5_error_code ret;
  91. char *confname;
  92. krb5_data data;
  93. *username = NULL;
  94. krb5_data_zero(&data);
  95. key->length = 0;
  96. key->data = NULL;
  97. ret = krb5_init_context(&context);
  98. if (ret)
  99. return ret;
  100. ret = krb5_cc_default(context, &id);
  101. if (ret)
  102. goto out;
  103. ret = krb5_cc_get_principal(context, id, &client);
  104. if (ret)
  105. goto out;
  106. ret = krb5_unparse_name_flags(context, client,
  107. KRB5_PRINCIPAL_UNPARSE_NO_REALM,
  108. username);
  109. krb5_free_principal(context, client);
  110. if (ret)
  111. goto out;
  112. asprintf(&confname, "ntlm-key-%s", name->domain);
  113. if (confname == NULL) {
  114. krb5_clear_error_message(context);
  115. ret = ENOMEM;
  116. goto out;
  117. }
  118. ret = krb5_cc_get_config(context, id, NULL,
  119. confname, &data);
  120. if (ret)
  121. goto out;
  122. key->data = malloc(data.length);
  123. if (key->data == NULL) {
  124. ret = ENOMEM;
  125. goto out;
  126. }
  127. key->length = data.length;
  128. memcpy(key->data, data.data, data.length);
  129. out:
  130. krb5_data_free(&data);
  131. if (id)
  132. krb5_cc_close(context, id);
  133. krb5_free_context(context);
  134. return ret;
  135. }
  136. int
  137. _gss_ntlm_get_user_cred(const ntlm_name target_name,
  138. ntlm_cred *rcred)
  139. {
  140. ntlm_cred cred;
  141. int ret;
  142. cred = calloc(1, sizeof(*cred));
  143. if (cred == NULL)
  144. return ENOMEM;
  145. ret = get_user_file(target_name, &cred->username, &cred->key);
  146. if (ret)
  147. ret = get_user_ccache(target_name, &cred->username, &cred->key);
  148. if (ret) {
  149. free(cred);
  150. return ret;
  151. }
  152. cred->domain = strdup(target_name->domain);
  153. *rcred = cred;
  154. return ret;
  155. }
  156. static int
  157. _gss_copy_cred(ntlm_cred from, ntlm_cred *to)
  158. {
  159. *to = calloc(1, sizeof(**to));
  160. if (*to == NULL)
  161. return ENOMEM;
  162. (*to)->username = strdup(from->username);
  163. if ((*to)->username == NULL) {
  164. free(*to);
  165. return ENOMEM;
  166. }
  167. (*to)->domain = strdup(from->domain);
  168. if ((*to)->domain == NULL) {
  169. free((*to)->username);
  170. free(*to);
  171. return ENOMEM;
  172. }
  173. (*to)->key.data = malloc(from->key.length);
  174. if ((*to)->key.data == NULL) {
  175. free((*to)->domain);
  176. free((*to)->username);
  177. free(*to);
  178. return ENOMEM;
  179. }
  180. memcpy((*to)->key.data, from->key.data, from->key.length);
  181. (*to)->key.length = from->key.length;
  182. return 0;
  183. }
  184. OM_uint32 GSSAPI_CALLCONV
  185. _gss_ntlm_init_sec_context
  186. (OM_uint32 * minor_status,
  187. const gss_cred_id_t initiator_cred_handle,
  188. gss_ctx_id_t * context_handle,
  189. const gss_name_t target_name,
  190. const gss_OID mech_type,
  191. OM_uint32 req_flags,
  192. OM_uint32 time_req,
  193. const gss_channel_bindings_t input_chan_bindings,
  194. const gss_buffer_t input_token,
  195. gss_OID * actual_mech_type,
  196. gss_buffer_t output_token,
  197. OM_uint32 * ret_flags,
  198. OM_uint32 * time_rec
  199. )
  200. {
  201. ntlm_ctx ctx;
  202. ntlm_name name = (ntlm_name)target_name;
  203. *minor_status = 0;
  204. if (ret_flags)
  205. *ret_flags = 0;
  206. if (time_rec)
  207. *time_rec = 0;
  208. if (actual_mech_type)
  209. *actual_mech_type = GSS_C_NO_OID;
  210. if (*context_handle == GSS_C_NO_CONTEXT) {
  211. struct ntlm_type1 type1;
  212. struct ntlm_buf data;
  213. uint32_t flags = 0;
  214. int ret;
  215. ctx = calloc(1, sizeof(*ctx));
  216. if (ctx == NULL) {
  217. *minor_status = EINVAL;
  218. return GSS_S_FAILURE;
  219. }
  220. *context_handle = (gss_ctx_id_t)ctx;
  221. if (initiator_cred_handle != GSS_C_NO_CREDENTIAL) {
  222. ntlm_cred cred = (ntlm_cred)initiator_cred_handle;
  223. ret = _gss_copy_cred(cred, &ctx->client);
  224. } else
  225. ret = _gss_ntlm_get_user_cred(name, &ctx->client);
  226. if (ret) {
  227. _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL);
  228. *minor_status = ret;
  229. return GSS_S_FAILURE;
  230. }
  231. if (req_flags & GSS_C_CONF_FLAG)
  232. flags |= NTLM_NEG_SEAL;
  233. if (req_flags & GSS_C_INTEG_FLAG)
  234. flags |= NTLM_NEG_SIGN;
  235. else
  236. flags |= NTLM_NEG_ALWAYS_SIGN;
  237. flags |= NTLM_NEG_UNICODE;
  238. flags |= NTLM_NEG_NTLM;
  239. flags |= NTLM_NEG_NTLM2_SESSION;
  240. flags |= NTLM_NEG_KEYEX;
  241. memset(&type1, 0, sizeof(type1));
  242. type1.flags = flags;
  243. type1.domain = name->domain;
  244. type1.hostname = NULL;
  245. type1.os[0] = 0;
  246. type1.os[1] = 0;
  247. ret = heim_ntlm_encode_type1(&type1, &data);
  248. if (ret) {
  249. _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL);
  250. *minor_status = ret;
  251. return GSS_S_FAILURE;
  252. }
  253. output_token->value = data.data;
  254. output_token->length = data.length;
  255. return GSS_S_CONTINUE_NEEDED;
  256. } else {
  257. krb5_error_code ret;
  258. struct ntlm_type2 type2;
  259. struct ntlm_type3 type3;
  260. struct ntlm_buf data;
  261. ctx = (ntlm_ctx)*context_handle;
  262. data.data = input_token->value;
  263. data.length = input_token->length;
  264. ret = heim_ntlm_decode_type2(&data, &type2);
  265. if (ret) {
  266. _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL);
  267. *minor_status = ret;
  268. return GSS_S_FAILURE;
  269. }
  270. ctx->flags = type2.flags;
  271. /* XXX check that type2.targetinfo matches `target_name´ */
  272. /* XXX check verify targetinfo buffer */
  273. memset(&type3, 0, sizeof(type3));
  274. type3.username = ctx->client->username;
  275. type3.flags = type2.flags;
  276. type3.targetname = type2.targetname;
  277. type3.ws = rk_UNCONST("workstation");
  278. /*
  279. * NTLM Version 1 if no targetinfo buffer.
  280. */
  281. if (1 || type2.targetinfo.length == 0) {
  282. struct ntlm_buf sessionkey;
  283. if (type2.flags & NTLM_NEG_NTLM2_SESSION) {
  284. unsigned char nonce[8];
  285. if (RAND_bytes(nonce, sizeof(nonce)) != 1) {
  286. _gss_ntlm_delete_sec_context(minor_status,
  287. context_handle, NULL);
  288. *minor_status = EINVAL;
  289. return GSS_S_FAILURE;
  290. }
  291. ret = heim_ntlm_calculate_ntlm2_sess(nonce,
  292. type2.challenge,
  293. ctx->client->key.data,
  294. &type3.lm,
  295. &type3.ntlm);
  296. } else {
  297. ret = heim_ntlm_calculate_ntlm1(ctx->client->key.data,
  298. ctx->client->key.length,
  299. type2.challenge,
  300. &type3.ntlm);
  301. }
  302. if (ret) {
  303. _gss_ntlm_delete_sec_context(minor_status,context_handle,NULL);
  304. *minor_status = ret;
  305. return GSS_S_FAILURE;
  306. }
  307. ret = heim_ntlm_build_ntlm1_master(ctx->client->key.data,
  308. ctx->client->key.length,
  309. &sessionkey,
  310. &type3.sessionkey);
  311. if (ret) {
  312. if (type3.lm.data)
  313. free(type3.lm.data);
  314. if (type3.ntlm.data)
  315. free(type3.ntlm.data);
  316. _gss_ntlm_delete_sec_context(minor_status,context_handle,NULL);
  317. *minor_status = ret;
  318. return GSS_S_FAILURE;
  319. }
  320. ret = krb5_data_copy(&ctx->sessionkey,
  321. sessionkey.data, sessionkey.length);
  322. free(sessionkey.data);
  323. if (ret) {
  324. if (type3.lm.data)
  325. free(type3.lm.data);
  326. if (type3.ntlm.data)
  327. free(type3.ntlm.data);
  328. _gss_ntlm_delete_sec_context(minor_status,context_handle,NULL);
  329. *minor_status = ret;
  330. return GSS_S_FAILURE;
  331. }
  332. ctx->status |= STATUS_SESSIONKEY;
  333. } else {
  334. struct ntlm_buf sessionkey;
  335. unsigned char ntlmv2[16];
  336. struct ntlm_targetinfo ti;
  337. /* verify infotarget */
  338. ret = heim_ntlm_decode_targetinfo(&type2.targetinfo, 1, &ti);
  339. if(ret) {
  340. _gss_ntlm_delete_sec_context(minor_status,
  341. context_handle, NULL);
  342. *minor_status = ret;
  343. return GSS_S_FAILURE;
  344. }
  345. if (ti.domainname && strcmp(ti.domainname, name->domain) != 0) {
  346. _gss_ntlm_delete_sec_context(minor_status,
  347. context_handle, NULL);
  348. *minor_status = EINVAL;
  349. return GSS_S_FAILURE;
  350. }
  351. ret = heim_ntlm_calculate_ntlm2(ctx->client->key.data,
  352. ctx->client->key.length,
  353. ctx->client->username,
  354. name->domain,
  355. type2.challenge,
  356. &type2.targetinfo,
  357. ntlmv2,
  358. &type3.ntlm);
  359. if (ret) {
  360. _gss_ntlm_delete_sec_context(minor_status,
  361. context_handle, NULL);
  362. *minor_status = ret;
  363. return GSS_S_FAILURE;
  364. }
  365. ret = heim_ntlm_build_ntlm1_master(ntlmv2, sizeof(ntlmv2),
  366. &sessionkey,
  367. &type3.sessionkey);
  368. memset(ntlmv2, 0, sizeof(ntlmv2));
  369. if (ret) {
  370. _gss_ntlm_delete_sec_context(minor_status,
  371. context_handle, NULL);
  372. *minor_status = ret;
  373. return GSS_S_FAILURE;
  374. }
  375. ctx->flags |= NTLM_NEG_NTLM2_SESSION;
  376. ret = krb5_data_copy(&ctx->sessionkey,
  377. sessionkey.data, sessionkey.length);
  378. free(sessionkey.data);
  379. if (ret) {
  380. _gss_ntlm_delete_sec_context(minor_status,
  381. context_handle, NULL);
  382. *minor_status = ret;
  383. return GSS_S_FAILURE;
  384. }
  385. }
  386. if (ctx->flags & NTLM_NEG_NTLM2_SESSION) {
  387. ctx->status |= STATUS_SESSIONKEY;
  388. _gss_ntlm_set_key(&ctx->u.v2.send, 0, (ctx->flags & NTLM_NEG_KEYEX),
  389. ctx->sessionkey.data,
  390. ctx->sessionkey.length);
  391. _gss_ntlm_set_key(&ctx->u.v2.recv, 1, (ctx->flags & NTLM_NEG_KEYEX),
  392. ctx->sessionkey.data,
  393. ctx->sessionkey.length);
  394. } else {
  395. ctx->status |= STATUS_SESSIONKEY;
  396. RC4_set_key(&ctx->u.v1.crypto_recv.key,
  397. ctx->sessionkey.length,
  398. ctx->sessionkey.data);
  399. RC4_set_key(&ctx->u.v1.crypto_send.key,
  400. ctx->sessionkey.length,
  401. ctx->sessionkey.data);
  402. }
  403. ret = heim_ntlm_encode_type3(&type3, &data);
  404. free(type3.sessionkey.data);
  405. if (type3.lm.data)
  406. free(type3.lm.data);
  407. if (type3.ntlm.data)
  408. free(type3.ntlm.data);
  409. if (ret) {
  410. _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL);
  411. *minor_status = ret;
  412. return GSS_S_FAILURE;
  413. }
  414. output_token->length = data.length;
  415. output_token->value = data.data;
  416. if (actual_mech_type)
  417. *actual_mech_type = GSS_NTLM_MECHANISM;
  418. if (ret_flags)
  419. *ret_flags = 0;
  420. if (time_rec)
  421. *time_rec = GSS_C_INDEFINITE;
  422. ctx->status |= STATUS_OPEN;
  423. return GSS_S_COMPLETE;
  424. }
  425. }