PageRenderTime 59ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/release/src-rt-6.x.4708/router/samba3/source3/winbindd/winbindd_cred_cache.c

https://bitbucket.org/oglop/tomato-arm-kille72
C | 1065 lines | 728 code | 167 blank | 170 comment | 119 complexity | db63ec587d4673af74ed0d8ba4d5eeb9 MD5 | raw file
Possible License(s): LGPL-2.0, LGPL-3.0, MPL-2.0-no-copyleft-exception, 0BSD, BSD-3-Clause, WTFPL, CC-BY-SA-3.0, LGPL-2.1, GPL-3.0, GPL-2.0
  1. /*
  2. Unix SMB/CIFS implementation.
  3. Winbind daemon - krb5 credential cache functions
  4. and in-memory cache functions.
  5. Copyright (C) Guenther Deschner 2005-2006
  6. Copyright (C) Jeremy Allison 2006
  7. This program is free software; you can redistribute it and/or modify
  8. it under the terms of the GNU General Public License as published by
  9. the Free Software Foundation; either version 3 of the License, or
  10. (at your option) any later version.
  11. This program is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. GNU General Public License for more details.
  15. You should have received a copy of the GNU General Public License
  16. along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include "includes.h"
  19. #include "winbindd.h"
  20. #include "../libcli/auth/libcli_auth.h"
  21. #include "smb_krb5.h"
  22. #include "libads/kerberos_proto.h"
  23. #undef DBGC_CLASS
  24. #define DBGC_CLASS DBGC_WINBIND
  25. /* uncomment this to do fast debugging on the krb5 ticket renewal event */
  26. #ifdef DEBUG_KRB5_TKT_RENEWAL
  27. #undef DEBUG_KRB5_TKT_RENEWAL
  28. #endif
  29. #define MAX_CCACHES 100
  30. static struct WINBINDD_CCACHE_ENTRY *ccache_list;
  31. static void krb5_ticket_gain_handler(struct event_context *,
  32. struct timed_event *,
  33. struct timeval,
  34. void *);
  35. static void add_krb5_ticket_gain_handler_event(struct WINBINDD_CCACHE_ENTRY *,
  36. struct timeval);
  37. /* The Krb5 ticket refresh handler should be scheduled
  38. at one-half of the period from now till the tkt
  39. expiration */
  40. static time_t krb5_event_refresh_time(time_t end_time)
  41. {
  42. time_t rest = end_time - time(NULL);
  43. return end_time - rest/2;
  44. }
  45. /****************************************************************
  46. Find an entry by name.
  47. ****************************************************************/
  48. static struct WINBINDD_CCACHE_ENTRY *get_ccache_by_username(const char *username)
  49. {
  50. struct WINBINDD_CCACHE_ENTRY *entry;
  51. for (entry = ccache_list; entry; entry = entry->next) {
  52. if (strequal(entry->username, username)) {
  53. return entry;
  54. }
  55. }
  56. return NULL;
  57. }
  58. /****************************************************************
  59. How many do we have ?
  60. ****************************************************************/
  61. static int ccache_entry_count(void)
  62. {
  63. struct WINBINDD_CCACHE_ENTRY *entry;
  64. int i = 0;
  65. for (entry = ccache_list; entry; entry = entry->next) {
  66. i++;
  67. }
  68. return i;
  69. }
  70. void ccache_remove_all_after_fork(void)
  71. {
  72. struct WINBINDD_CCACHE_ENTRY *cur, *next;
  73. for (cur = ccache_list; cur; cur = next) {
  74. next = cur->next;
  75. DLIST_REMOVE(ccache_list, cur);
  76. TALLOC_FREE(cur->event);
  77. TALLOC_FREE(cur);
  78. }
  79. return;
  80. }
  81. /****************************************************************
  82. Do the work of refreshing the ticket.
  83. ****************************************************************/
  84. static void krb5_ticket_refresh_handler(struct event_context *event_ctx,
  85. struct timed_event *te,
  86. struct timeval now,
  87. void *private_data)
  88. {
  89. struct WINBINDD_CCACHE_ENTRY *entry =
  90. talloc_get_type_abort(private_data, struct WINBINDD_CCACHE_ENTRY);
  91. #ifdef HAVE_KRB5
  92. int ret;
  93. time_t new_start;
  94. time_t expire_time = 0;
  95. struct WINBINDD_MEMORY_CREDS *cred_ptr = entry->cred_ptr;
  96. #endif
  97. DEBUG(10,("krb5_ticket_refresh_handler called\n"));
  98. DEBUGADD(10,("event called for: %s, %s\n",
  99. entry->ccname, entry->username));
  100. TALLOC_FREE(entry->event);
  101. #ifdef HAVE_KRB5
  102. /* Kinit again if we have the user password and we can't renew the old
  103. * tgt anymore
  104. * NB
  105. * This happens when machine are put to sleep for a very long time. */
  106. if (entry->renew_until < time(NULL)) {
  107. rekinit:
  108. if (cred_ptr && cred_ptr->pass) {
  109. set_effective_uid(entry->uid);
  110. ret = kerberos_kinit_password_ext(entry->principal_name,
  111. cred_ptr->pass,
  112. 0, /* hm, can we do time correction here ? */
  113. &entry->refresh_time,
  114. &entry->renew_until,
  115. entry->ccname,
  116. False, /* no PAC required anymore */
  117. True,
  118. WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
  119. NULL);
  120. gain_root_privilege();
  121. if (ret) {
  122. DEBUG(3,("krb5_ticket_refresh_handler: "
  123. "could not re-kinit: %s\n",
  124. error_message(ret)));
  125. /* destroy the ticket because we cannot rekinit
  126. * it, ignore error here */
  127. ads_kdestroy(entry->ccname);
  128. /* Don't break the ticket refresh chain: retry
  129. * refreshing ticket sometime later when KDC is
  130. * unreachable -- BoYang. More error code handling
  131. * here?
  132. * */
  133. if ((ret == KRB5_KDC_UNREACH)
  134. || (ret == KRB5_REALM_CANT_RESOLVE)) {
  135. #if defined(DEBUG_KRB5_TKT_RENEWAL)
  136. new_start = time(NULL) + 30;
  137. #else
  138. new_start = time(NULL) +
  139. MAX(30, lp_winbind_cache_time());
  140. #endif
  141. add_krb5_ticket_gain_handler_event(entry,
  142. timeval_set(new_start, 0));
  143. return;
  144. }
  145. TALLOC_FREE(entry->event);
  146. return;
  147. }
  148. DEBUG(10,("krb5_ticket_refresh_handler: successful re-kinit "
  149. "for: %s in ccache: %s\n",
  150. entry->principal_name, entry->ccname));
  151. #if defined(DEBUG_KRB5_TKT_RENEWAL)
  152. new_start = time(NULL) + 30;
  153. #else
  154. /* The tkt should be refreshed at one-half the period
  155. from now to the expiration time */
  156. expire_time = entry->refresh_time;
  157. new_start = krb5_event_refresh_time(entry->refresh_time);
  158. #endif
  159. goto done;
  160. } else {
  161. /* can this happen?
  162. * No cached credentials
  163. * destroy ticket and refresh chain
  164. * */
  165. ads_kdestroy(entry->ccname);
  166. TALLOC_FREE(entry->event);
  167. return;
  168. }
  169. }
  170. set_effective_uid(entry->uid);
  171. ret = smb_krb5_renew_ticket(entry->ccname,
  172. entry->principal_name,
  173. entry->service,
  174. &new_start);
  175. #if defined(DEBUG_KRB5_TKT_RENEWAL)
  176. new_start = time(NULL) + 30;
  177. #else
  178. expire_time = new_start;
  179. new_start = krb5_event_refresh_time(new_start);
  180. #endif
  181. gain_root_privilege();
  182. if (ret) {
  183. DEBUG(3,("krb5_ticket_refresh_handler: "
  184. "could not renew tickets: %s\n",
  185. error_message(ret)));
  186. /* maybe we are beyond the renewing window */
  187. /* evil rises here, we refresh ticket failed,
  188. * but the ticket might be expired. Therefore,
  189. * When we refresh ticket failed, destory the
  190. * ticket */
  191. ads_kdestroy(entry->ccname);
  192. /* avoid breaking the renewal chain: retry in
  193. * lp_winbind_cache_time() seconds when the KDC was not
  194. * available right now.
  195. * the return code can be KRB5_REALM_CANT_RESOLVE.
  196. * More error code handling here? */
  197. if ((ret == KRB5_KDC_UNREACH)
  198. || (ret == KRB5_REALM_CANT_RESOLVE)) {
  199. #if defined(DEBUG_KRB5_TKT_RENEWAL)
  200. new_start = time(NULL) + 30;
  201. #else
  202. new_start = time(NULL) +
  203. MAX(30, lp_winbind_cache_time());
  204. #endif
  205. /* ticket is destroyed here, we have to regain it
  206. * if it is possible */
  207. add_krb5_ticket_gain_handler_event(entry,
  208. timeval_set(new_start, 0));
  209. return;
  210. }
  211. /* This is evil, if the ticket was already expired.
  212. * renew ticket function returns KRB5KRB_AP_ERR_TKT_EXPIRED.
  213. * But there is still a chance that we can rekinit it.
  214. *
  215. * This happens when user login in online mode, and then network
  216. * down or something cause winbind goes offline for a very long time,
  217. * and then goes online again. ticket expired, renew failed.
  218. * This happens when machine are put to sleep for a long time,
  219. * but shorter than entry->renew_util.
  220. * NB
  221. * Looks like the KDC is reachable, we want to rekinit as soon as
  222. * possible instead of waiting some time later. */
  223. if ((ret == KRB5KRB_AP_ERR_TKT_EXPIRED)
  224. || (ret == KRB5_FCC_NOFILE)) goto rekinit;
  225. return;
  226. }
  227. done:
  228. /* in cases that ticket will be unrenewable soon, we don't try to renew ticket
  229. * but try to regain ticket if it is possible */
  230. if (entry->renew_until && expire_time
  231. && (entry->renew_until <= expire_time)) {
  232. /* try to regain ticket 10 seconds before expiration */
  233. expire_time -= 10;
  234. add_krb5_ticket_gain_handler_event(entry,
  235. timeval_set(expire_time, 0));
  236. return;
  237. }
  238. if (entry->refresh_time == 0) {
  239. entry->refresh_time = new_start;
  240. }
  241. entry->event = event_add_timed(winbind_event_context(), entry,
  242. timeval_set(new_start, 0),
  243. krb5_ticket_refresh_handler,
  244. entry);
  245. #endif
  246. }
  247. /****************************************************************
  248. Do the work of regaining a ticket when coming from offline auth.
  249. ****************************************************************/
  250. static void krb5_ticket_gain_handler(struct event_context *event_ctx,
  251. struct timed_event *te,
  252. struct timeval now,
  253. void *private_data)
  254. {
  255. struct WINBINDD_CCACHE_ENTRY *entry =
  256. talloc_get_type_abort(private_data, struct WINBINDD_CCACHE_ENTRY);
  257. #ifdef HAVE_KRB5
  258. int ret;
  259. struct timeval t;
  260. struct WINBINDD_MEMORY_CREDS *cred_ptr = entry->cred_ptr;
  261. struct winbindd_domain *domain = NULL;
  262. #endif
  263. DEBUG(10,("krb5_ticket_gain_handler called\n"));
  264. DEBUGADD(10,("event called for: %s, %s\n",
  265. entry->ccname, entry->username));
  266. TALLOC_FREE(entry->event);
  267. #ifdef HAVE_KRB5
  268. if (!cred_ptr || !cred_ptr->pass) {
  269. DEBUG(10,("krb5_ticket_gain_handler: no memory creds\n"));
  270. return;
  271. }
  272. if ((domain = find_domain_from_name(entry->realm)) == NULL) {
  273. DEBUG(0,("krb5_ticket_gain_handler: unknown domain\n"));
  274. return;
  275. }
  276. if (!domain->online) {
  277. goto retry_later;
  278. }
  279. set_effective_uid(entry->uid);
  280. ret = kerberos_kinit_password_ext(entry->principal_name,
  281. cred_ptr->pass,
  282. 0, /* hm, can we do time correction here ? */
  283. &entry->refresh_time,
  284. &entry->renew_until,
  285. entry->ccname,
  286. False, /* no PAC required anymore */
  287. True,
  288. WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
  289. NULL);
  290. gain_root_privilege();
  291. if (ret) {
  292. DEBUG(3,("krb5_ticket_gain_handler: "
  293. "could not kinit: %s\n",
  294. error_message(ret)));
  295. /* evil. If we cannot do it, destroy any the __maybe__
  296. * __existing__ ticket */
  297. ads_kdestroy(entry->ccname);
  298. goto retry_later;
  299. }
  300. DEBUG(10,("krb5_ticket_gain_handler: "
  301. "successful kinit for: %s in ccache: %s\n",
  302. entry->principal_name, entry->ccname));
  303. goto got_ticket;
  304. retry_later:
  305. #if defined(DEBUG_KRB5_TKT_RENEWAL)
  306. t = timeval_set(time(NULL) + 30, 0);
  307. #else
  308. t = timeval_current_ofs(MAX(30, lp_winbind_cache_time()), 0);
  309. #endif
  310. add_krb5_ticket_gain_handler_event(entry, t);
  311. return;
  312. got_ticket:
  313. #if defined(DEBUG_KRB5_TKT_RENEWAL)
  314. t = timeval_set(time(NULL) + 30, 0);
  315. #else
  316. t = timeval_set(krb5_event_refresh_time(entry->refresh_time), 0);
  317. #endif
  318. if (entry->refresh_time == 0) {
  319. entry->refresh_time = t.tv_sec;
  320. }
  321. entry->event = event_add_timed(winbind_event_context(),
  322. entry,
  323. t,
  324. krb5_ticket_refresh_handler,
  325. entry);
  326. return;
  327. #endif
  328. }
  329. /**************************************************************
  330. The gain initial ticket case is recognised as entry->refresh_time
  331. is always zero.
  332. **************************************************************/
  333. static void add_krb5_ticket_gain_handler_event(struct WINBINDD_CCACHE_ENTRY *entry,
  334. struct timeval t)
  335. {
  336. entry->refresh_time = 0;
  337. entry->event = event_add_timed(winbind_event_context(),
  338. entry,
  339. t,
  340. krb5_ticket_gain_handler,
  341. entry);
  342. }
  343. void ccache_regain_all_now(void)
  344. {
  345. struct WINBINDD_CCACHE_ENTRY *cur;
  346. struct timeval t = timeval_current();
  347. for (cur = ccache_list; cur; cur = cur->next) {
  348. struct timed_event *new_event;
  349. /*
  350. * if refresh_time is 0, we know that the
  351. * the event has the krb5_ticket_gain_handler
  352. */
  353. if (cur->refresh_time == 0) {
  354. new_event = event_add_timed(winbind_event_context(),
  355. cur,
  356. t,
  357. krb5_ticket_gain_handler,
  358. cur);
  359. } else {
  360. new_event = event_add_timed(winbind_event_context(),
  361. cur,
  362. t,
  363. krb5_ticket_refresh_handler,
  364. cur);
  365. }
  366. if (!new_event) {
  367. continue;
  368. }
  369. TALLOC_FREE(cur->event);
  370. cur->event = new_event;
  371. }
  372. return;
  373. }
  374. /****************************************************************
  375. Check if an ccache entry exists.
  376. ****************************************************************/
  377. bool ccache_entry_exists(const char *username)
  378. {
  379. struct WINBINDD_CCACHE_ENTRY *entry = get_ccache_by_username(username);
  380. return (entry != NULL);
  381. }
  382. /****************************************************************
  383. Ensure we're changing the correct entry.
  384. ****************************************************************/
  385. bool ccache_entry_identical(const char *username,
  386. uid_t uid,
  387. const char *ccname)
  388. {
  389. struct WINBINDD_CCACHE_ENTRY *entry = get_ccache_by_username(username);
  390. if (!entry) {
  391. return False;
  392. }
  393. if (entry->uid != uid) {
  394. DEBUG(0,("cache_entry_identical: uid's differ: %u != %u\n",
  395. (unsigned int)entry->uid, (unsigned int)uid));
  396. return False;
  397. }
  398. if (!strcsequal(entry->ccname, ccname)) {
  399. DEBUG(0,("cache_entry_identical: "
  400. "ccnames differ: (cache) %s != (client) %s\n",
  401. entry->ccname, ccname));
  402. return False;
  403. }
  404. return True;
  405. }
  406. NTSTATUS add_ccache_to_list(const char *princ_name,
  407. const char *ccname,
  408. const char *service,
  409. const char *username,
  410. const char *pass,
  411. const char *realm,
  412. uid_t uid,
  413. time_t create_time,
  414. time_t ticket_end,
  415. time_t renew_until,
  416. bool postponed_request)
  417. {
  418. struct WINBINDD_CCACHE_ENTRY *entry = NULL;
  419. struct timeval t;
  420. NTSTATUS ntret;
  421. #ifdef HAVE_KRB5
  422. int ret;
  423. #endif
  424. if ((username == NULL && princ_name == NULL) ||
  425. ccname == NULL || uid < 0) {
  426. return NT_STATUS_INVALID_PARAMETER;
  427. }
  428. if (ccache_entry_count() + 1 > MAX_CCACHES) {
  429. DEBUG(10,("add_ccache_to_list: "
  430. "max number of ccaches reached\n"));
  431. return NT_STATUS_NO_MORE_ENTRIES;
  432. }
  433. /* If it is cached login, destroy krb5 ticket
  434. * to avoid surprise. */
  435. #ifdef HAVE_KRB5
  436. if (postponed_request) {
  437. /* ignore KRB5_FCC_NOFILE error here */
  438. ret = ads_kdestroy(ccname);
  439. if (ret == KRB5_FCC_NOFILE) {
  440. ret = 0;
  441. }
  442. if (ret) {
  443. DEBUG(0, ("add_ccache_to_list: failed to destroy "
  444. "user krb5 ccache %s with %s\n", ccname,
  445. error_message(ret)));
  446. return krb5_to_nt_status(ret);
  447. }
  448. DEBUG(10, ("add_ccache_to_list: successfully destroyed "
  449. "krb5 ccache %s for user %s\n", ccname,
  450. username));
  451. }
  452. #endif
  453. /* Reference count old entries */
  454. entry = get_ccache_by_username(username);
  455. if (entry) {
  456. /* Check cached entries are identical. */
  457. if (!ccache_entry_identical(username, uid, ccname)) {
  458. return NT_STATUS_INVALID_PARAMETER;
  459. }
  460. entry->ref_count++;
  461. DEBUG(10,("add_ccache_to_list: "
  462. "ref count on entry %s is now %d\n",
  463. username, entry->ref_count));
  464. /* FIXME: in this case we still might want to have a krb5 cred
  465. * event handler created - gd
  466. * Add ticket refresh handler here */
  467. if (!lp_winbind_refresh_tickets() || renew_until <= 0) {
  468. return NT_STATUS_OK;
  469. }
  470. if (!entry->event) {
  471. if (postponed_request) {
  472. t = timeval_current_ofs(MAX(30, lp_winbind_cache_time()), 0);
  473. add_krb5_ticket_gain_handler_event(entry, t);
  474. } else {
  475. /* Renew at 1/2 the ticket expiration time */
  476. #if defined(DEBUG_KRB5_TKT_RENEWAL)
  477. t = timeval_set(time(NULL)+30, 0);
  478. #else
  479. t = timeval_set(krb5_event_refresh_time(ticket_end),
  480. 0);
  481. #endif
  482. if (!entry->refresh_time) {
  483. entry->refresh_time = t.tv_sec;
  484. }
  485. entry->event = event_add_timed(winbind_event_context(),
  486. entry,
  487. t,
  488. krb5_ticket_refresh_handler,
  489. entry);
  490. }
  491. if (!entry->event) {
  492. ntret = remove_ccache(username);
  493. if (!NT_STATUS_IS_OK(ntret)) {
  494. DEBUG(0, ("add_ccache_to_list: Failed to remove krb5 "
  495. "ccache %s for user %s\n", entry->ccname,
  496. entry->username));
  497. DEBUG(0, ("add_ccache_to_list: error is %s\n",
  498. nt_errstr(ntret)));
  499. return ntret;
  500. }
  501. return NT_STATUS_NO_MEMORY;
  502. }
  503. DEBUG(10,("add_ccache_to_list: added krb5_ticket handler\n"));
  504. }
  505. /*
  506. * If we're set up to renew our krb5 tickets, we must
  507. * cache the credentials in memory for the ticket
  508. * renew function (or increase the reference count
  509. * if we're logging in more than once). Fix inspired
  510. * by patch from Ian Gordon <ian.gordon@strath.ac.uk>
  511. * for bugid #9098.
  512. */
  513. ntret = winbindd_add_memory_creds(username, uid, pass);
  514. DEBUG(10, ("winbindd_add_memory_creds returned: %s\n",
  515. nt_errstr(ntret)));
  516. return NT_STATUS_OK;
  517. }
  518. entry = TALLOC_P(NULL, struct WINBINDD_CCACHE_ENTRY);
  519. if (!entry) {
  520. return NT_STATUS_NO_MEMORY;
  521. }
  522. ZERO_STRUCTP(entry);
  523. if (username) {
  524. entry->username = talloc_strdup(entry, username);
  525. if (!entry->username) {
  526. goto no_mem;
  527. }
  528. }
  529. if (princ_name) {
  530. entry->principal_name = talloc_strdup(entry, princ_name);
  531. if (!entry->principal_name) {
  532. goto no_mem;
  533. }
  534. }
  535. if (service) {
  536. entry->service = talloc_strdup(entry, service);
  537. if (!entry->service) {
  538. goto no_mem;
  539. }
  540. }
  541. entry->ccname = talloc_strdup(entry, ccname);
  542. if (!entry->ccname) {
  543. goto no_mem;
  544. }
  545. entry->realm = talloc_strdup(entry, realm);
  546. if (!entry->realm) {
  547. goto no_mem;
  548. }
  549. entry->create_time = create_time;
  550. entry->renew_until = renew_until;
  551. entry->uid = uid;
  552. entry->ref_count = 1;
  553. if (!lp_winbind_refresh_tickets() || renew_until <= 0) {
  554. goto add_entry;
  555. }
  556. if (postponed_request) {
  557. t = timeval_current_ofs(MAX(30, lp_winbind_cache_time()), 0);
  558. add_krb5_ticket_gain_handler_event(entry, t);
  559. } else {
  560. /* Renew at 1/2 the ticket expiration time */
  561. #if defined(DEBUG_KRB5_TKT_RENEWAL)
  562. t = timeval_set(time(NULL)+30, 0);
  563. #else
  564. t = timeval_set(krb5_event_refresh_time(ticket_end), 0);
  565. #endif
  566. if (entry->refresh_time == 0) {
  567. entry->refresh_time = t.tv_sec;
  568. }
  569. entry->event = event_add_timed(winbind_event_context(),
  570. entry,
  571. t,
  572. krb5_ticket_refresh_handler,
  573. entry);
  574. }
  575. if (!entry->event) {
  576. goto no_mem;
  577. }
  578. DEBUG(10,("add_ccache_to_list: added krb5_ticket handler\n"));
  579. add_entry:
  580. DLIST_ADD(ccache_list, entry);
  581. DEBUG(10,("add_ccache_to_list: "
  582. "added ccache [%s] for user [%s] to the list\n",
  583. ccname, username));
  584. if (entry->event) {
  585. /*
  586. * If we're set up to renew our krb5 tickets, we must
  587. * cache the credentials in memory for the ticket
  588. * renew function. Fix inspired by patch from
  589. * Ian Gordon <ian.gordon@strath.ac.uk> for
  590. * bugid #9098.
  591. */
  592. ntret = winbindd_add_memory_creds(username, uid, pass);
  593. DEBUG(10, ("winbindd_add_memory_creds returned: %s\n",
  594. nt_errstr(ntret)));
  595. }
  596. return NT_STATUS_OK;
  597. no_mem:
  598. TALLOC_FREE(entry);
  599. return NT_STATUS_NO_MEMORY;
  600. }
  601. /*******************************************************************
  602. Remove a WINBINDD_CCACHE_ENTRY entry and the krb5 ccache if no longer
  603. referenced.
  604. *******************************************************************/
  605. NTSTATUS remove_ccache(const char *username)
  606. {
  607. struct WINBINDD_CCACHE_ENTRY *entry = get_ccache_by_username(username);
  608. NTSTATUS status = NT_STATUS_OK;
  609. #ifdef HAVE_KRB5
  610. krb5_error_code ret;
  611. #endif
  612. if (!entry) {
  613. return NT_STATUS_OBJECT_NAME_NOT_FOUND;
  614. }
  615. if (entry->ref_count <= 0) {
  616. DEBUG(0,("remove_ccache: logic error. "
  617. "ref count for user %s = %d\n",
  618. username, entry->ref_count));
  619. return NT_STATUS_INTERNAL_DB_CORRUPTION;
  620. }
  621. entry->ref_count--;
  622. if (entry->ref_count > 0) {
  623. DEBUG(10,("remove_ccache: entry %s ref count now %d\n",
  624. username, entry->ref_count));
  625. return NT_STATUS_OK;
  626. }
  627. /* no references any more */
  628. DLIST_REMOVE(ccache_list, entry);
  629. TALLOC_FREE(entry->event); /* unregisters events */
  630. #ifdef HAVE_KRB5
  631. ret = ads_kdestroy(entry->ccname);
  632. /* we ignore the error when there has been no credential cache */
  633. if (ret == KRB5_FCC_NOFILE) {
  634. ret = 0;
  635. } else if (ret) {
  636. DEBUG(0,("remove_ccache: "
  637. "failed to destroy user krb5 ccache %s with: %s\n",
  638. entry->ccname, error_message(ret)));
  639. } else {
  640. DEBUG(10,("remove_ccache: "
  641. "successfully destroyed krb5 ccache %s for user %s\n",
  642. entry->ccname, username));
  643. }
  644. status = krb5_to_nt_status(ret);
  645. #endif
  646. TALLOC_FREE(entry);
  647. DEBUG(10,("remove_ccache: removed ccache for user %s\n", username));
  648. return status;
  649. }
  650. /*******************************************************************
  651. In memory credentials cache code.
  652. *******************************************************************/
  653. static struct WINBINDD_MEMORY_CREDS *memory_creds_list;
  654. /***********************************************************
  655. Find an entry on the list by name.
  656. ***********************************************************/
  657. struct WINBINDD_MEMORY_CREDS *find_memory_creds_by_name(const char *username)
  658. {
  659. struct WINBINDD_MEMORY_CREDS *p;
  660. for (p = memory_creds_list; p; p = p->next) {
  661. if (strequal(p->username, username)) {
  662. return p;
  663. }
  664. }
  665. return NULL;
  666. }
  667. /***********************************************************
  668. Store the required creds and mlock them.
  669. ***********************************************************/
  670. static NTSTATUS store_memory_creds(struct WINBINDD_MEMORY_CREDS *memcredp,
  671. const char *pass)
  672. {
  673. #if !defined(HAVE_MLOCK)
  674. return NT_STATUS_OK;
  675. #else
  676. /* new_entry->nt_hash is the base pointer for the block
  677. of memory pointed into by new_entry->lm_hash and
  678. new_entry->pass (if we're storing plaintext). */
  679. memcredp->len = NT_HASH_LEN + LM_HASH_LEN;
  680. if (pass) {
  681. memcredp->len += strlen(pass)+1;
  682. }
  683. #if defined(LINUX)
  684. /* aligning the memory on on x86_64 and compiling
  685. with gcc 4.1 using -O2 causes a segv in the
  686. next memset() --jerry */
  687. memcredp->nt_hash = SMB_MALLOC_ARRAY(unsigned char, memcredp->len);
  688. #else
  689. /* On non-linux platforms, mlock()'d memory must be aligned */
  690. memcredp->nt_hash = SMB_MEMALIGN_ARRAY(unsigned char,
  691. getpagesize(), memcredp->len);
  692. #endif
  693. if (!memcredp->nt_hash) {
  694. return NT_STATUS_NO_MEMORY;
  695. }
  696. memset(memcredp->nt_hash, 0x0, memcredp->len);
  697. memcredp->lm_hash = memcredp->nt_hash + NT_HASH_LEN;
  698. #ifdef DEBUG_PASSWORD
  699. DEBUG(10,("mlocking memory: %p\n", memcredp->nt_hash));
  700. #endif
  701. if ((mlock(memcredp->nt_hash, memcredp->len)) == -1) {
  702. DEBUG(0,("failed to mlock memory: %s (%d)\n",
  703. strerror(errno), errno));
  704. SAFE_FREE(memcredp->nt_hash);
  705. return map_nt_error_from_unix(errno);
  706. }
  707. #ifdef DEBUG_PASSWORD
  708. DEBUG(10,("mlocked memory: %p\n", memcredp->nt_hash));
  709. #endif
  710. /* Create and store the password hashes. */
  711. E_md4hash(pass, memcredp->nt_hash);
  712. E_deshash(pass, memcredp->lm_hash);
  713. if (pass) {
  714. memcredp->pass = (char *)memcredp->lm_hash + LM_HASH_LEN;
  715. memcpy(memcredp->pass, pass,
  716. memcredp->len - NT_HASH_LEN - LM_HASH_LEN);
  717. }
  718. return NT_STATUS_OK;
  719. #endif
  720. }
  721. /***********************************************************
  722. Destroy existing creds.
  723. ***********************************************************/
  724. static NTSTATUS delete_memory_creds(struct WINBINDD_MEMORY_CREDS *memcredp)
  725. {
  726. #if !defined(HAVE_MUNLOCK)
  727. return NT_STATUS_OK;
  728. #else
  729. if (munlock(memcredp->nt_hash, memcredp->len) == -1) {
  730. DEBUG(0,("failed to munlock memory: %s (%d)\n",
  731. strerror(errno), errno));
  732. return map_nt_error_from_unix(errno);
  733. }
  734. memset(memcredp->nt_hash, '\0', memcredp->len);
  735. SAFE_FREE(memcredp->nt_hash);
  736. memcredp->nt_hash = NULL;
  737. memcredp->lm_hash = NULL;
  738. memcredp->pass = NULL;
  739. memcredp->len = 0;
  740. return NT_STATUS_OK;
  741. #endif
  742. }
  743. /***********************************************************
  744. Replace the required creds with new ones (password change).
  745. ***********************************************************/
  746. static NTSTATUS winbindd_replace_memory_creds_internal(struct WINBINDD_MEMORY_CREDS *memcredp,
  747. const char *pass)
  748. {
  749. NTSTATUS status = delete_memory_creds(memcredp);
  750. if (!NT_STATUS_IS_OK(status)) {
  751. return status;
  752. }
  753. return store_memory_creds(memcredp, pass);
  754. }
  755. /*************************************************************
  756. Store credentials in memory in a list.
  757. *************************************************************/
  758. static NTSTATUS winbindd_add_memory_creds_internal(const char *username,
  759. uid_t uid,
  760. const char *pass)
  761. {
  762. /* Shortcut to ensure we don't store if no mlock. */
  763. #if !defined(HAVE_MLOCK) || !defined(HAVE_MUNLOCK)
  764. return NT_STATUS_OK;
  765. #else
  766. NTSTATUS status;
  767. struct WINBINDD_MEMORY_CREDS *memcredp = NULL;
  768. memcredp = find_memory_creds_by_name(username);
  769. if (uid == (uid_t)-1) {
  770. DEBUG(0,("winbindd_add_memory_creds_internal: "
  771. "invalid uid for user %s.\n", username));
  772. return NT_STATUS_INVALID_PARAMETER;
  773. }
  774. if (memcredp) {
  775. /* Already exists. Increment the reference count and replace stored creds. */
  776. if (uid != memcredp->uid) {
  777. DEBUG(0,("winbindd_add_memory_creds_internal: "
  778. "uid %u for user %s doesn't "
  779. "match stored uid %u. Replacing.\n",
  780. (unsigned int)uid, username,
  781. (unsigned int)memcredp->uid));
  782. memcredp->uid = uid;
  783. }
  784. memcredp->ref_count++;
  785. DEBUG(10,("winbindd_add_memory_creds_internal: "
  786. "ref count for user %s is now %d\n",
  787. username, memcredp->ref_count));
  788. return winbindd_replace_memory_creds_internal(memcredp, pass);
  789. }
  790. memcredp = TALLOC_ZERO_P(NULL, struct WINBINDD_MEMORY_CREDS);
  791. if (!memcredp) {
  792. return NT_STATUS_NO_MEMORY;
  793. }
  794. memcredp->username = talloc_strdup(memcredp, username);
  795. if (!memcredp->username) {
  796. talloc_destroy(memcredp);
  797. return NT_STATUS_NO_MEMORY;
  798. }
  799. status = store_memory_creds(memcredp, pass);
  800. if (!NT_STATUS_IS_OK(status)) {
  801. talloc_destroy(memcredp);
  802. return status;
  803. }
  804. memcredp->uid = uid;
  805. memcredp->ref_count = 1;
  806. DLIST_ADD(memory_creds_list, memcredp);
  807. DEBUG(10,("winbindd_add_memory_creds_internal: "
  808. "added entry for user %s\n", username));
  809. return NT_STATUS_OK;
  810. #endif
  811. }
  812. /*************************************************************
  813. Store users credentials in memory. If we also have a
  814. struct WINBINDD_CCACHE_ENTRY for this username with a
  815. refresh timer, then store the plaintext of the password
  816. and associate the new credentials with the struct WINBINDD_CCACHE_ENTRY.
  817. *************************************************************/
  818. NTSTATUS winbindd_add_memory_creds(const char *username,
  819. uid_t uid,
  820. const char *pass)
  821. {
  822. struct WINBINDD_CCACHE_ENTRY *entry = get_ccache_by_username(username);
  823. NTSTATUS status;
  824. status = winbindd_add_memory_creds_internal(username, uid, pass);
  825. if (!NT_STATUS_IS_OK(status)) {
  826. return status;
  827. }
  828. if (entry) {
  829. struct WINBINDD_MEMORY_CREDS *memcredp = NULL;
  830. memcredp = find_memory_creds_by_name(username);
  831. if (memcredp) {
  832. entry->cred_ptr = memcredp;
  833. }
  834. }
  835. return status;
  836. }
  837. /*************************************************************
  838. Decrement the in-memory ref count - delete if zero.
  839. *************************************************************/
  840. NTSTATUS winbindd_delete_memory_creds(const char *username)
  841. {
  842. struct WINBINDD_MEMORY_CREDS *memcredp = NULL;
  843. struct WINBINDD_CCACHE_ENTRY *entry = NULL;
  844. NTSTATUS status = NT_STATUS_OK;
  845. memcredp = find_memory_creds_by_name(username);
  846. entry = get_ccache_by_username(username);
  847. if (!memcredp) {
  848. DEBUG(10,("winbindd_delete_memory_creds: unknown user %s\n",
  849. username));
  850. return NT_STATUS_OBJECT_NAME_NOT_FOUND;
  851. }
  852. if (memcredp->ref_count <= 0) {
  853. DEBUG(0,("winbindd_delete_memory_creds: logic error. "
  854. "ref count for user %s = %d\n",
  855. username, memcredp->ref_count));
  856. status = NT_STATUS_INTERNAL_DB_CORRUPTION;
  857. }
  858. memcredp->ref_count--;
  859. if (memcredp->ref_count <= 0) {
  860. delete_memory_creds(memcredp);
  861. DLIST_REMOVE(memory_creds_list, memcredp);
  862. talloc_destroy(memcredp);
  863. DEBUG(10,("winbindd_delete_memory_creds: "
  864. "deleted entry for user %s\n",
  865. username));
  866. } else {
  867. DEBUG(10,("winbindd_delete_memory_creds: "
  868. "entry for user %s ref_count now %d\n",
  869. username, memcredp->ref_count));
  870. }
  871. if (entry) {
  872. /* Ensure we have no dangling references to this. */
  873. entry->cred_ptr = NULL;
  874. }
  875. return status;
  876. }
  877. /***********************************************************
  878. Replace the required creds with new ones (password change).
  879. ***********************************************************/
  880. NTSTATUS winbindd_replace_memory_creds(const char *username,
  881. const char *pass)
  882. {
  883. struct WINBINDD_MEMORY_CREDS *memcredp = NULL;
  884. memcredp = find_memory_creds_by_name(username);
  885. if (!memcredp) {
  886. DEBUG(10,("winbindd_replace_memory_creds: unknown user %s\n",
  887. username));
  888. return NT_STATUS_OBJECT_NAME_NOT_FOUND;
  889. }
  890. DEBUG(10,("winbindd_replace_memory_creds: replaced creds for user %s\n",
  891. username));
  892. return winbindd_replace_memory_creds_internal(memcredp, pass);
  893. }