00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025 #include "includes.h"
00026 #include "winbindd.h"
00027 #undef DBGC_CLASS
00028 #define DBGC_CLASS DBGC_WINBIND
00029
00030
00031 #ifdef DEBUG_KRB5_TKT_RENEWAL
00032 #undef DEBUG_KRB5_TKT_RENEWAL
00033 #endif
00034
00035 #define MAX_CCACHES 100
00036
00037 static struct WINBINDD_CCACHE_ENTRY *ccache_list;
00038
00039
00040
00041
00042 #define KRB5_EVENT_REFRESH_TIME(x) ((x) - (((x) - time(NULL))/2))
00043
00044
00045
00046
00047
00048 static struct WINBINDD_CCACHE_ENTRY *get_ccache_by_username(const char *username)
00049 {
00050 struct WINBINDD_CCACHE_ENTRY *entry;
00051
00052 for (entry = ccache_list; entry; entry = entry->next) {
00053 if (strequal(entry->username, username)) {
00054 return entry;
00055 }
00056 }
00057 return NULL;
00058 }
00059
00060
00061
00062
00063
00064 static int ccache_entry_count(void)
00065 {
00066 struct WINBINDD_CCACHE_ENTRY *entry;
00067 int i = 0;
00068
00069 for (entry = ccache_list; entry; entry = entry->next) {
00070 i++;
00071 }
00072 return i;
00073 }
00074
00075
00076
00077
00078
00079 static void krb5_ticket_refresh_handler(struct event_context *event_ctx,
00080 struct timed_event *te,
00081 const struct timeval *now,
00082 void *private_data)
00083 {
00084 struct WINBINDD_CCACHE_ENTRY *entry =
00085 talloc_get_type_abort(private_data, struct WINBINDD_CCACHE_ENTRY);
00086 #ifdef HAVE_KRB5
00087 int ret;
00088 time_t new_start;
00089 struct WINBINDD_MEMORY_CREDS *cred_ptr = entry->cred_ptr;
00090 #endif
00091
00092 DEBUG(10,("krb5_ticket_refresh_handler called\n"));
00093 DEBUGADD(10,("event called for: %s, %s\n", entry->ccname, entry->username));
00094
00095 TALLOC_FREE(entry->event);
00096
00097 #ifdef HAVE_KRB5
00098
00099
00100
00101
00102 if ((entry->renew_until < time(NULL)) && cred_ptr && cred_ptr->pass) {
00103
00104 set_effective_uid(entry->uid);
00105
00106 ret = kerberos_kinit_password_ext(entry->principal_name,
00107 cred_ptr->pass,
00108 0,
00109 &entry->refresh_time,
00110 &entry->renew_until,
00111 entry->ccname,
00112 False,
00113 True,
00114 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME);
00115 gain_root_privilege();
00116
00117 if (ret) {
00118 DEBUG(3,("krb5_ticket_refresh_handler: could not re-kinit: %s\n",
00119 error_message(ret)));
00120 TALLOC_FREE(entry->event);
00121 return;
00122 }
00123
00124 DEBUG(10,("krb5_ticket_refresh_handler: successful re-kinit "
00125 "for: %s in ccache: %s\n",
00126 entry->principal_name, entry->ccname));
00127
00128 #if defined(DEBUG_KRB5_TKT_RENEWAL)
00129 new_start = time(NULL) + 30;
00130 #else
00131
00132
00133 new_start = KRB5_EVENT_REFRESH_TIME(entry->refresh_time);
00134 #endif
00135
00136 goto done;
00137 }
00138
00139 set_effective_uid(entry->uid);
00140
00141 ret = smb_krb5_renew_ticket(entry->ccname,
00142 entry->principal_name,
00143 entry->service,
00144 &new_start);
00145 #if defined(DEBUG_KRB5_TKT_RENEWAL)
00146 new_start = time(NULL) + 30;
00147 #else
00148 new_start = KRB5_EVENT_REFRESH_TIME(new_start);
00149 #endif
00150
00151 gain_root_privilege();
00152
00153 if (ret) {
00154 DEBUG(3,("krb5_ticket_refresh_handler: could not renew tickets: %s\n",
00155 error_message(ret)));
00156
00157
00158
00159
00160
00161 if (ret == KRB5_KDC_UNREACH) {
00162 new_start = time(NULL) + MAX(30, lp_winbind_cache_time());
00163 goto done;
00164 }
00165
00166 return;
00167 }
00168
00169 done:
00170
00171 entry->event = event_add_timed(winbind_event_context(), entry,
00172 timeval_set(new_start, 0),
00173 "krb5_ticket_refresh_handler",
00174 krb5_ticket_refresh_handler,
00175 entry);
00176
00177 #endif
00178 }
00179
00180
00181
00182
00183
00184 static void krb5_ticket_gain_handler(struct event_context *event_ctx,
00185 struct timed_event *te,
00186 const struct timeval *now,
00187 void *private_data)
00188 {
00189 struct WINBINDD_CCACHE_ENTRY *entry =
00190 talloc_get_type_abort(private_data, struct WINBINDD_CCACHE_ENTRY);
00191 #ifdef HAVE_KRB5
00192 int ret;
00193 struct timeval t;
00194 struct WINBINDD_MEMORY_CREDS *cred_ptr = entry->cred_ptr;
00195 struct winbindd_domain *domain = NULL;
00196 #endif
00197
00198 DEBUG(10,("krb5_ticket_gain_handler called\n"));
00199 DEBUGADD(10,("event called for: %s, %s\n", entry->ccname, entry->username));
00200
00201 TALLOC_FREE(entry->event);
00202
00203 #ifdef HAVE_KRB5
00204
00205 if (!cred_ptr || !cred_ptr->pass) {
00206 DEBUG(10,("krb5_ticket_gain_handler: no memory creds\n"));
00207 return;
00208 }
00209
00210 if ((domain = find_domain_from_name(entry->realm)) == NULL) {
00211 DEBUG(0,("krb5_ticket_gain_handler: unknown domain\n"));
00212 return;
00213 }
00214
00215 if (domain->online) {
00216
00217 set_effective_uid(entry->uid);
00218
00219 ret = kerberos_kinit_password_ext(entry->principal_name,
00220 cred_ptr->pass,
00221 0,
00222 &entry->refresh_time,
00223 &entry->renew_until,
00224 entry->ccname,
00225 False,
00226 True,
00227 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME);
00228 gain_root_privilege();
00229
00230 if (ret) {
00231 DEBUG(3,("krb5_ticket_gain_handler: could not kinit: %s\n",
00232 error_message(ret)));
00233 goto retry_later;
00234 }
00235
00236 DEBUG(10,("krb5_ticket_gain_handler: successful kinit for: %s in ccache: %s\n",
00237 entry->principal_name, entry->ccname));
00238
00239 goto got_ticket;
00240 }
00241
00242 retry_later:
00243
00244 entry->event = event_add_timed(winbind_event_context(), entry,
00245 timeval_current_ofs(MAX(30, lp_winbind_cache_time()), 0),
00246 "krb5_ticket_gain_handler",
00247 krb5_ticket_gain_handler,
00248 entry);
00249
00250 return;
00251
00252 got_ticket:
00253
00254 #if defined(DEBUG_KRB5_TKT_RENEWAL)
00255 t = timeval_set(time(NULL) + 30, 0);
00256 #else
00257 t = timeval_set(KRB5_EVENT_REFRESH_TIME(entry->refresh_time), 0);
00258 #endif
00259
00260 entry->event = event_add_timed(winbind_event_context(), entry,
00261 t,
00262 "krb5_ticket_refresh_handler",
00263 krb5_ticket_refresh_handler,
00264 entry);
00265
00266 return;
00267 #endif
00268 }
00269
00270
00271
00272
00273
00274 BOOL ccache_entry_exists(const char *username)
00275 {
00276 struct WINBINDD_CCACHE_ENTRY *entry = get_ccache_by_username(username);
00277 return (entry != NULL);
00278 }
00279
00280
00281
00282
00283
00284 BOOL ccache_entry_identical(const char *username, uid_t uid, const char *ccname)
00285 {
00286 struct WINBINDD_CCACHE_ENTRY *entry = get_ccache_by_username(username);
00287
00288 if (!entry) {
00289 return False;
00290 }
00291
00292 if (entry->uid != uid) {
00293 DEBUG(0,("cache_entry_identical: uid's differ: %u != %u\n",
00294 (unsigned int)entry->uid, (unsigned int)uid ));
00295 return False;
00296 }
00297 if (!strcsequal(entry->ccname, ccname)) {
00298 DEBUG(0,("cache_entry_identical: ccnames differ: (cache) %s != (client) %s\n",
00299 entry->ccname, ccname));
00300 return False;
00301 }
00302 return True;
00303 }
00304
00305 NTSTATUS add_ccache_to_list(const char *princ_name,
00306 const char *ccname,
00307 const char *service,
00308 const char *username,
00309 const char *realm,
00310 uid_t uid,
00311 time_t create_time,
00312 time_t ticket_end,
00313 time_t renew_until,
00314 BOOL postponed_request)
00315 {
00316 struct WINBINDD_CCACHE_ENTRY *entry = NULL;
00317
00318 if ((username == NULL && princ_name == NULL) || ccname == NULL || uid < 0) {
00319 return NT_STATUS_INVALID_PARAMETER;
00320 }
00321
00322 if (ccache_entry_count() + 1 > MAX_CCACHES) {
00323 DEBUG(10,("add_ccache_to_list: max number of ccaches reached\n"));
00324 return NT_STATUS_NO_MORE_ENTRIES;
00325 }
00326
00327
00328 entry = get_ccache_by_username(username);
00329 if (entry) {
00330
00331 if (!ccache_entry_identical(username, uid, ccname)) {
00332 return NT_STATUS_INVALID_PARAMETER;
00333 }
00334 entry->ref_count++;
00335 DEBUG(10,("add_ccache_to_list: ref count on entry %s is now %d\n",
00336 username, entry->ref_count));
00337
00338
00339 return NT_STATUS_OK;
00340 }
00341
00342 entry = TALLOC_P(NULL, struct WINBINDD_CCACHE_ENTRY);
00343 if (!entry) {
00344 return NT_STATUS_NO_MEMORY;
00345 }
00346
00347 ZERO_STRUCTP(entry);
00348
00349 if (username) {
00350 entry->username = talloc_strdup(entry, username);
00351 if (!entry->username) {
00352 goto no_mem;
00353 }
00354 }
00355 if (princ_name) {
00356 entry->principal_name = talloc_strdup(entry, princ_name);
00357 if (!entry->principal_name) {
00358 goto no_mem;
00359 }
00360 }
00361 if (service) {
00362 entry->service = talloc_strdup(entry, service);
00363 if (!entry->service) {
00364 goto no_mem;
00365 }
00366 }
00367
00368 entry->ccname = talloc_strdup(entry, ccname);
00369 if (!entry->ccname) {
00370 goto no_mem;
00371 }
00372
00373 entry->realm = talloc_strdup(entry, realm);
00374 if (!entry->realm) {
00375 goto no_mem;
00376 }
00377
00378 entry->create_time = create_time;
00379 entry->renew_until = renew_until;
00380 entry->uid = uid;
00381 entry->ref_count = 1;
00382
00383 if (lp_winbind_refresh_tickets() && renew_until > 0) {
00384 if (postponed_request) {
00385 entry->event = event_add_timed(winbind_event_context(), entry,
00386 timeval_current_ofs(MAX(30, lp_winbind_cache_time()), 0),
00387 "krb5_ticket_gain_handler",
00388 krb5_ticket_gain_handler,
00389 entry);
00390 } else {
00391
00392 entry->event = event_add_timed(winbind_event_context(), entry,
00393 #if defined(DEBUG_KRB5_TKT_RENEWAL)
00394 timeval_set(time(NULL)+30, 0),
00395 #else
00396 timeval_set(KRB5_EVENT_REFRESH_TIME(ticket_end), 0),
00397 #endif
00398 "krb5_ticket_refresh_handler",
00399 krb5_ticket_refresh_handler,
00400 entry);
00401 }
00402
00403 if (!entry->event) {
00404 goto no_mem;
00405 }
00406
00407 DEBUG(10,("add_ccache_to_list: added krb5_ticket handler\n"));
00408 }
00409
00410 DLIST_ADD(ccache_list, entry);
00411
00412 DEBUG(10,("add_ccache_to_list: added ccache [%s] for user [%s] to the list\n", ccname, username));
00413
00414 return NT_STATUS_OK;
00415
00416 no_mem:
00417
00418 TALLOC_FREE(entry);
00419 return NT_STATUS_NO_MEMORY;
00420 }
00421
00422
00423
00424
00425
00426 NTSTATUS remove_ccache(const char *username)
00427 {
00428 struct WINBINDD_CCACHE_ENTRY *entry = get_ccache_by_username(username);
00429 NTSTATUS status = NT_STATUS_OK;
00430 #ifdef HAVE_KRB5
00431 krb5_error_code ret;
00432 #endif
00433
00434 if (!entry) {
00435 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
00436 }
00437
00438 if (entry->ref_count <= 0) {
00439 DEBUG(0,("remove_ccache: logic error. ref count for user %s = %d\n",
00440 username, entry->ref_count));
00441 return NT_STATUS_INTERNAL_DB_CORRUPTION;
00442 }
00443
00444 entry->ref_count--;
00445
00446 if (entry->ref_count > 0) {
00447 DEBUG(10,("remove_ccache: entry %s ref count now %d\n",
00448 username, entry->ref_count ));
00449 return NT_STATUS_OK;
00450 }
00451
00452
00453
00454 DLIST_REMOVE(ccache_list, entry);
00455 TALLOC_FREE(entry->event);
00456
00457 #ifdef HAVE_KRB5
00458 ret = ads_kdestroy(entry->ccname);
00459
00460
00461 if (ret == KRB5_FCC_NOFILE) {
00462 ret = 0;
00463 } else if (ret) {
00464 DEBUG(0,("remove_ccache: failed to destroy user krb5 ccache %s with: %s\n",
00465 entry->ccname, error_message(ret)));
00466 } else {
00467 DEBUG(10,("remove_ccache: successfully destroyed krb5 ccache %s for user %s\n",
00468 entry->ccname, username));
00469 }
00470 status = krb5_to_nt_status(ret);
00471 #endif
00472
00473 TALLOC_FREE(entry);
00474 DEBUG(10,("remove_ccache: removed ccache for user %s\n", username));
00475
00476 return status;
00477 }
00478
00479
00480
00481
00482
00483 static struct WINBINDD_MEMORY_CREDS *memory_creds_list;
00484
00485
00486
00487
00488
00489 struct WINBINDD_MEMORY_CREDS *find_memory_creds_by_name(const char *username)
00490 {
00491 struct WINBINDD_MEMORY_CREDS *p;
00492
00493 for (p = memory_creds_list; p; p = p->next) {
00494 if (strequal(p->username, username)) {
00495 return p;
00496 }
00497 }
00498 return NULL;
00499 }
00500
00501
00502
00503
00504
00505 static NTSTATUS store_memory_creds(struct WINBINDD_MEMORY_CREDS *memcredp, const char *pass)
00506 {
00507 #if !defined(HAVE_MLOCK)
00508 return NT_STATUS_OK;
00509 #else
00510
00511
00512
00513
00514 memcredp->len = NT_HASH_LEN + LM_HASH_LEN;
00515 if (pass) {
00516 memcredp->len += strlen(pass)+1;
00517 }
00518
00519
00520 #if defined(LINUX)
00521
00522
00523
00524 memcredp->nt_hash = SMB_MALLOC_ARRAY(unsigned char, memcredp->len);
00525 #else
00526
00527 memcredp->nt_hash = SMB_MEMALIGN_ARRAY(unsigned char,
00528 getpagesize(), memcredp->len);
00529 #endif
00530 if (!memcredp->nt_hash) {
00531 return NT_STATUS_NO_MEMORY;
00532 }
00533 memset( memcredp->nt_hash, 0x0, memcredp->len );
00534
00535 memcredp->lm_hash = memcredp->nt_hash + NT_HASH_LEN;
00536
00537 #ifdef DEBUG_PASSWORD
00538 DEBUG(10,("mlocking memory: %p\n", memcredp->nt_hash));
00539 #endif
00540 if ((mlock(memcredp->nt_hash, memcredp->len)) == -1) {
00541 DEBUG(0,("failed to mlock memory: %s (%d)\n",
00542 strerror(errno), errno));
00543 SAFE_FREE(memcredp->nt_hash);
00544 return map_nt_error_from_unix(errno);
00545 }
00546
00547 #ifdef DEBUG_PASSWORD
00548 DEBUG(10,("mlocked memory: %p\n", memcredp->nt_hash));
00549 #endif
00550
00551
00552 E_md4hash(pass, memcredp->nt_hash);
00553 E_deshash(pass, memcredp->lm_hash);
00554
00555 if (pass) {
00556 memcredp->pass = (char *)memcredp->lm_hash + LM_HASH_LEN;
00557 memcpy(memcredp->pass, pass, memcredp->len - NT_HASH_LEN - LM_HASH_LEN);
00558 }
00559
00560 return NT_STATUS_OK;
00561 #endif
00562 }
00563
00564
00565
00566
00567
00568 static NTSTATUS delete_memory_creds(struct WINBINDD_MEMORY_CREDS *memcredp)
00569 {
00570 #if !defined(HAVE_MUNLOCK)
00571 return NT_STATUS_OK;
00572 #else
00573 if (munlock(memcredp->nt_hash, memcredp->len) == -1) {
00574 DEBUG(0,("failed to munlock memory: %s (%d)\n",
00575 strerror(errno), errno));
00576 return map_nt_error_from_unix(errno);
00577 }
00578 memset(memcredp->nt_hash, '\0', memcredp->len);
00579 SAFE_FREE(memcredp->nt_hash);
00580 memcredp->nt_hash = NULL;
00581 memcredp->lm_hash = NULL;
00582 memcredp->pass = NULL;
00583 memcredp->len = 0;
00584 return NT_STATUS_OK;
00585 #endif
00586 }
00587
00588
00589
00590
00591
00592 static NTSTATUS winbindd_replace_memory_creds_internal(struct WINBINDD_MEMORY_CREDS *memcredp,
00593 const char *pass)
00594 {
00595 NTSTATUS status = delete_memory_creds(memcredp);
00596 if (!NT_STATUS_IS_OK(status)) {
00597 return status;
00598 }
00599 return store_memory_creds(memcredp, pass);
00600 }
00601
00602
00603
00604
00605
00606 static NTSTATUS winbindd_add_memory_creds_internal(const char *username, uid_t uid, const char *pass)
00607 {
00608
00609 #if !defined(HAVE_MLOCK) || !defined(HAVE_MUNLOCK)
00610 return NT_STATUS_OK;
00611 #else
00612 NTSTATUS status;
00613 struct WINBINDD_MEMORY_CREDS *memcredp = find_memory_creds_by_name(username);
00614
00615 if (uid == (uid_t)-1) {
00616 DEBUG(0,("winbindd_add_memory_creds_internal: invalid uid for user %s.\n",
00617 username ));
00618 return NT_STATUS_INVALID_PARAMETER;
00619 }
00620
00621 if (memcredp) {
00622
00623 if (uid != memcredp->uid) {
00624 DEBUG(0,("winbindd_add_memory_creds_internal: uid %u for user %s doesn't "
00625 "match stored uid %u. Replacing.\n",
00626 (unsigned int)uid, username, (unsigned int)memcredp->uid ));
00627 memcredp->uid = uid;
00628 }
00629 memcredp->ref_count++;
00630 DEBUG(10,("winbindd_add_memory_creds_internal: ref count for user %s is now %d\n",
00631 username, memcredp->ref_count ));
00632 return winbindd_replace_memory_creds_internal(memcredp, pass);
00633 }
00634
00635 memcredp = TALLOC_ZERO_P(NULL, struct WINBINDD_MEMORY_CREDS);
00636 if (!memcredp) {
00637 return NT_STATUS_NO_MEMORY;
00638 }
00639 memcredp->username = talloc_strdup(memcredp, username);
00640 if (!memcredp->username) {
00641 talloc_destroy(memcredp);
00642 return NT_STATUS_NO_MEMORY;
00643 }
00644
00645 status = store_memory_creds(memcredp, pass);
00646 if (!NT_STATUS_IS_OK(status)) {
00647 talloc_destroy(memcredp);
00648 return status;
00649 }
00650
00651 memcredp->uid = uid;
00652 memcredp->ref_count = 1;
00653 DLIST_ADD(memory_creds_list, memcredp);
00654
00655 DEBUG(10,("winbindd_add_memory_creds_internal: added entry for user %s\n",
00656 username ));
00657
00658 return NT_STATUS_OK;
00659 #endif
00660 }
00661
00662
00663
00664
00665
00666
00667
00668
00669 NTSTATUS winbindd_add_memory_creds(const char *username, uid_t uid, const char *pass)
00670 {
00671 struct WINBINDD_CCACHE_ENTRY *entry = get_ccache_by_username(username);
00672 NTSTATUS status;
00673
00674 status = winbindd_add_memory_creds_internal(username, uid, pass);
00675 if (!NT_STATUS_IS_OK(status)) {
00676 return status;
00677 }
00678
00679 if (entry) {
00680 struct WINBINDD_MEMORY_CREDS *memcredp = find_memory_creds_by_name(username);
00681 if (memcredp) {
00682 entry->cred_ptr = memcredp;
00683 }
00684 }
00685
00686 return status;
00687 }
00688
00689
00690
00691
00692
00693 NTSTATUS winbindd_delete_memory_creds(const char *username)
00694 {
00695 struct WINBINDD_MEMORY_CREDS *memcredp = find_memory_creds_by_name(username);
00696 struct WINBINDD_CCACHE_ENTRY *entry = get_ccache_by_username(username);
00697 NTSTATUS status = NT_STATUS_OK;
00698
00699 if (!memcredp) {
00700 DEBUG(10,("winbindd_delete_memory_creds: unknown user %s\n",
00701 username ));
00702 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
00703 }
00704
00705 if (memcredp->ref_count <= 0) {
00706 DEBUG(0,("winbindd_delete_memory_creds: logic error. ref count for user %s = %d\n",
00707 username, memcredp->ref_count));
00708 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
00709 }
00710
00711 memcredp->ref_count--;
00712 if (memcredp->ref_count <= 0) {
00713 delete_memory_creds(memcredp);
00714 DLIST_REMOVE(memory_creds_list, memcredp);
00715 talloc_destroy(memcredp);
00716 DEBUG(10,("winbindd_delete_memory_creds: deleted entry for user %s\n",
00717 username));
00718 } else {
00719 DEBUG(10,("winbindd_delete_memory_creds: entry for user %s ref_count now %d\n",
00720 username, memcredp->ref_count));
00721 }
00722
00723 if (entry) {
00724 entry->cred_ptr = NULL;
00725 }
00726 return status;
00727 }
00728
00729
00730
00731
00732
00733 NTSTATUS winbindd_replace_memory_creds(const char *username, const char *pass)
00734 {
00735 struct WINBINDD_MEMORY_CREDS *memcredp = find_memory_creds_by_name(username);
00736
00737 if (!memcredp) {
00738 DEBUG(10,("winbindd_replace_memory_creds: unknown user %s\n",
00739 username ));
00740 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
00741 }
00742
00743 DEBUG(10,("winbindd_replace_memory_creds: replaced creds for user %s\n",
00744 username ));
00745
00746 return winbindd_replace_memory_creds_internal(memcredp, pass);
00747 }