/** * Check if cached connection can be reused. If the connection cannot * be reused the ADS_STRUCT is freed and the pointer is set to NULL. */ static void ads_cached_connection_reuse(ADS_STRUCT **adsp) { ADS_STRUCT *ads = *adsp; if (ads != NULL) { time_t expire; time_t now = time(NULL); expire = MIN(ads->auth.tgt_expire, ads->auth.tgs_expire); DEBUG(7, ("Current tickets expire in %d seconds (at %d, time " "is now %d)\n", (uint32_t)expire - (uint32_t)now, (uint32_t) expire, (uint32_t) now)); if ( ads->config.realm && (expire > now)) { return; } else { /* we own this ADS_STRUCT so make sure it goes away */ DEBUG(7,("Deleting expired krb5 credential cache\n")); ads->is_mine = True; ads_destroy( &ads ); ads_kdestroy(WINBIND_CCACHE_NAME); *adsp = NULL; } } }
NTSTATUS remove_ccache(const char *username) { struct WINBINDD_CCACHE_ENTRY *entry = get_ccache_by_username(username); NTSTATUS status = NT_STATUS_OK; #ifdef HAVE_KRB5 krb5_error_code ret; #endif if (!entry) { return NT_STATUS_OBJECT_NAME_NOT_FOUND; } if (entry->ref_count <= 0) { DEBUG(0,("remove_ccache: logic error. " "ref count for user %s = %d\n", username, entry->ref_count)); return NT_STATUS_INTERNAL_DB_CORRUPTION; } entry->ref_count--; if (entry->ref_count > 0) { DEBUG(10,("remove_ccache: entry %s ref count now %d\n", username, entry->ref_count)); return NT_STATUS_OK; } /* no references any more */ DLIST_REMOVE(ccache_list, entry); TALLOC_FREE(entry->event); /* unregisters events */ #ifdef HAVE_KRB5 ret = ads_kdestroy(entry->ccname); /* we ignore the error when there has been no credential cache */ if (ret == KRB5_FCC_NOFILE) { ret = 0; } else if (ret) { DEBUG(0,("remove_ccache: " "failed to destroy user krb5 ccache %s with: %s\n", entry->ccname, error_message(ret))); } else { DEBUG(10,("remove_ccache: " "successfully destroyed krb5 ccache %s for user %s\n", entry->ccname, username)); } status = krb5_to_nt_status(ret); #endif TALLOC_FREE(entry); DEBUG(10,("remove_ccache: removed ccache for user %s\n", username)); return status; }
WERROR nt_printer_guid_retrieve(TALLOC_CTX *mem_ctx, const char *printer, struct GUID *pguid) { ADS_STRUCT *ads = NULL; char *old_krb5ccname = NULL; char *printer_dn; WERROR result; ADS_STATUS ads_status; TALLOC_CTX *tmp_ctx; tmp_ctx = talloc_new(mem_ctx); if (tmp_ctx == NULL) { return WERR_NOMEM; } ads = ads_init(lp_realm(), lp_workgroup(), NULL); if (ads == NULL) { result = WERR_SERVER_UNAVAILABLE; goto out; } old_krb5ccname = getenv(KRB5_ENV_CCNAME); setenv(KRB5_ENV_CCNAME, "MEMORY:prtpub_cache", 1); SAFE_FREE(ads->auth.password); ads->auth.password = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL); ads_status = ads_connect(ads); if (!ADS_ERR_OK(ads_status)) { DEBUG(3, ("ads_connect failed: %s\n", ads_errstr(ads_status))); result = WERR_ACCESS_DENIED; goto out; } result = nt_printer_dn_lookup(tmp_ctx, ads, printer, &printer_dn); if (!W_ERROR_IS_OK(result)) { goto out; } result = nt_printer_guid_retrieve_internal(ads, printer_dn, pguid); out: TALLOC_FREE(tmp_ctx); ads_destroy(&ads); ads_kdestroy("MEMORY:prtpub_cache"); unsetenv(KRB5_ENV_CCNAME); if (old_krb5ccname != NULL) { setenv(KRB5_ENV_CCNAME, old_krb5ccname, 0); } return result; }
/* return our ads connections structure for a domain. We keep the connection open to make things faster */ static ADS_STRUCT *ads_cached_connection(struct winbindd_domain *domain) { ADS_STRUCT *ads; ADS_STATUS status; fstring dc_name; struct sockaddr_storage dc_ss; DEBUG(10,("ads_cached_connection\n")); if (domain->private_data) { time_t expire; time_t now = time(NULL); /* check for a valid structure */ ads = (ADS_STRUCT *)domain->private_data; expire = MIN(ads->auth.tgt_expire, ads->auth.tgs_expire); DEBUG(7, ("Current tickets expire in %d seconds (at %d, time is now %d)\n", (uint32)expire-(uint32)now, (uint32) expire, (uint32) now)); if ( ads->config.realm && (expire > now)) { return ads; } else { /* we own this ADS_STRUCT so make sure it goes away */ DEBUG(7,("Deleting expired krb5 credential cache\n")); ads->is_mine = True; ads_destroy( &ads ); ads_kdestroy("MEMORY:winbind_ccache"); domain->private_data = NULL; } } /* we don't want this to affect the users ccache */ setenv("KRB5CCNAME", "MEMORY:winbind_ccache", 1); ads = ads_init(domain->alt_name, domain->name, NULL); if (!ads) { DEBUG(1,("ads_init for domain %s failed\n", domain->name)); return NULL; } /* the machine acct password might have change - fetch it every time */ SAFE_FREE(ads->auth.password); SAFE_FREE(ads->auth.realm); if ( IS_DC ) { if ( !pdb_get_trusteddom_pw( domain->name, &ads->auth.password, NULL, NULL ) ) { ads_destroy( &ads ); return NULL; } ads->auth.realm = SMB_STRDUP( ads->server.realm ); strupper_m( ads->auth.realm ); } else { struct winbindd_domain *our_domain = domain; ads->auth.password = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL); /* always give preference to the alt_name in our primary domain if possible */ if ( !domain->primary ) our_domain = find_our_domain(); if ( our_domain->alt_name[0] != '\0' ) { ads->auth.realm = SMB_STRDUP( our_domain->alt_name ); strupper_m( ads->auth.realm ); } else ads->auth.realm = SMB_STRDUP( lp_realm() ); } ads->auth.renewable = WINBINDD_PAM_AUTH_KRB5_RENEW_TIME; /* Setup the server affinity cache. We don't reaally care about the name. Just setup affinity and the KRB5_CONFIG file. */ get_dc_name( ads->server.workgroup, ads->server.realm, dc_name, &dc_ss ); status = ads_connect(ads); if (!ADS_ERR_OK(status) || !ads->config.realm) { DEBUG(1,("ads_connect for domain %s failed: %s\n", domain->name, ads_errstr(status))); ads_destroy(&ads); /* if we get ECONNREFUSED then it might be a NT4 server, fall back to MSRPC */ if (status.error_type == ENUM_ADS_ERROR_SYSTEM && status.err.rc == ECONNREFUSED) { /* 'reconnect_methods' is the MS-RPC backend. */ DEBUG(1,("Trying MSRPC methods\n")); domain->backend = &reconnect_methods; } return NULL; } /* set the flag that says we don't own the memory even though we do so that ads_destroy() won't destroy the structure we pass back by reference */ ads->is_mine = False; domain->private_data = (void *)ads; return ads; }
WERROR check_published_printers(struct messaging_context *msg_ctx) { ADS_STATUS ads_rc; ADS_STRUCT *ads = NULL; int snum; int n_services = lp_numservices(); TALLOC_CTX *tmp_ctx = NULL; struct auth_serversupplied_info *session_info = NULL; struct spoolss_PrinterInfo2 *pinfo2; NTSTATUS status; WERROR result; tmp_ctx = talloc_new(NULL); if (!tmp_ctx) return WERR_NOMEM; ads = ads_init(lp_realm(), lp_workgroup(), NULL); if (!ads) { DEBUG(3, ("ads_init() failed\n")); return WERR_SERVER_UNAVAILABLE; } setenv(KRB5_ENV_CCNAME, "MEMORY:prtpub_cache", 1); SAFE_FREE(ads->auth.password); ads->auth.password = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL); /* ads_connect() will find the DC for us */ ads_rc = ads_connect(ads); if (!ADS_ERR_OK(ads_rc)) { DEBUG(3, ("ads_connect failed: %s\n", ads_errstr(ads_rc))); result = WERR_ACCESS_DENIED; goto done; } status = make_session_info_system(tmp_ctx, &session_info); if (!NT_STATUS_IS_OK(status)) { DEBUG(0, ("check_published_printers: " "Could not create system session_info\n")); result = WERR_ACCESS_DENIED; goto done; } for (snum = 0; snum < n_services; snum++) { if (!lp_snum_ok(snum) || !lp_print_ok(snum)) { continue; } result = winreg_get_printer(tmp_ctx, session_info, msg_ctx, lp_servicename(snum), &pinfo2); if (!W_ERROR_IS_OK(result)) { continue; } if (pinfo2->attributes & PRINTER_ATTRIBUTE_PUBLISHED) { nt_printer_publish_ads(msg_ctx, ads, pinfo2); } TALLOC_FREE(pinfo2); } result = WERR_OK; done: ads_destroy(&ads); ads_kdestroy("MEMORY:prtpub_cache"); talloc_free(tmp_ctx); return result; }
NTSTATUS add_ccache_to_list(const char *princ_name, const char *ccname, const char *service, const char *username, const char *pass, const char *realm, uid_t uid, time_t create_time, time_t ticket_end, time_t renew_until, bool postponed_request) { struct WINBINDD_CCACHE_ENTRY *entry = NULL; struct timeval t; NTSTATUS ntret; #ifdef HAVE_KRB5 int ret; #endif if ((username == NULL && princ_name == NULL) || ccname == NULL || uid < 0) { return NT_STATUS_INVALID_PARAMETER; } if (ccache_entry_count() + 1 > MAX_CCACHES) { DEBUG(10,("add_ccache_to_list: " "max number of ccaches reached\n")); return NT_STATUS_NO_MORE_ENTRIES; } /* If it is cached login, destroy krb5 ticket * to avoid surprise. */ #ifdef HAVE_KRB5 if (postponed_request) { /* ignore KRB5_FCC_NOFILE error here */ ret = ads_kdestroy(ccname); if (ret == KRB5_FCC_NOFILE) { ret = 0; } if (ret) { DEBUG(0, ("add_ccache_to_list: failed to destroy " "user krb5 ccache %s with %s\n", ccname, error_message(ret))); return krb5_to_nt_status(ret); } DEBUG(10, ("add_ccache_to_list: successfully destroyed " "krb5 ccache %s for user %s\n", ccname, username)); } #endif /* Reference count old entries */ entry = get_ccache_by_username(username); if (entry) { /* Check cached entries are identical. */ if (!ccache_entry_identical(username, uid, ccname)) { return NT_STATUS_INVALID_PARAMETER; } entry->ref_count++; DEBUG(10,("add_ccache_to_list: " "ref count on entry %s is now %d\n", username, entry->ref_count)); /* FIXME: in this case we still might want to have a krb5 cred * event handler created - gd * Add ticket refresh handler here */ if (!lp_winbind_refresh_tickets() || renew_until <= 0) { return NT_STATUS_OK; } if (!entry->event) { if (postponed_request) { t = timeval_current_ofs(MAX(30, lp_winbind_cache_time()), 0); add_krb5_ticket_gain_handler_event(entry, t); } else { /* Renew at 1/2 the ticket expiration time */ #if defined(DEBUG_KRB5_TKT_RENEWAL) t = timeval_set(time(NULL)+30, 0); #else t = timeval_set(krb5_event_refresh_time(ticket_end), 0); #endif if (!entry->refresh_time) { entry->refresh_time = t.tv_sec; } entry->event = tevent_add_timer(winbind_event_context(), entry, t, krb5_ticket_refresh_handler, entry); } if (!entry->event) { ntret = remove_ccache(username); if (!NT_STATUS_IS_OK(ntret)) { DEBUG(0, ("add_ccache_to_list: Failed to remove krb5 " "ccache %s for user %s\n", entry->ccname, entry->username)); DEBUG(0, ("add_ccache_to_list: error is %s\n", nt_errstr(ntret))); return ntret; } return NT_STATUS_NO_MEMORY; } DEBUG(10,("add_ccache_to_list: added krb5_ticket handler\n")); } /* * If we're set up to renew our krb5 tickets, we must * cache the credentials in memory for the ticket * renew function (or increase the reference count * if we're logging in more than once). Fix inspired * by patch from Ian Gordon <*****@*****.**> * for bugid #9098. */ ntret = winbindd_add_memory_creds(username, uid, pass); DEBUG(10, ("winbindd_add_memory_creds returned: %s\n", nt_errstr(ntret))); return NT_STATUS_OK; } entry = talloc(NULL, struct WINBINDD_CCACHE_ENTRY); if (!entry) { return NT_STATUS_NO_MEMORY; } ZERO_STRUCTP(entry); if (username) { entry->username = talloc_strdup(entry, username); if (!entry->username) { goto no_mem; } } if (princ_name) { entry->principal_name = talloc_strdup(entry, princ_name); if (!entry->principal_name) { goto no_mem; } } if (service) { entry->service = talloc_strdup(entry, service); if (!entry->service) { goto no_mem; } } entry->ccname = talloc_strdup(entry, ccname); if (!entry->ccname) { goto no_mem; } entry->realm = talloc_strdup(entry, realm); if (!entry->realm) { goto no_mem; } entry->create_time = create_time; entry->renew_until = renew_until; entry->uid = uid; entry->ref_count = 1; if (!lp_winbind_refresh_tickets() || renew_until <= 0) { goto add_entry; } if (postponed_request) { t = timeval_current_ofs(MAX(30, lp_winbind_cache_time()), 0); add_krb5_ticket_gain_handler_event(entry, t); } else { /* Renew at 1/2 the ticket expiration time */ #if defined(DEBUG_KRB5_TKT_RENEWAL) t = timeval_set(time(NULL)+30, 0); #else t = timeval_set(krb5_event_refresh_time(ticket_end), 0); #endif if (entry->refresh_time == 0) { entry->refresh_time = t.tv_sec; } entry->event = tevent_add_timer(winbind_event_context(), entry, t, krb5_ticket_refresh_handler, entry); } if (!entry->event) { goto no_mem; } DEBUG(10,("add_ccache_to_list: added krb5_ticket handler\n")); add_entry: DLIST_ADD(ccache_list, entry); DEBUG(10,("add_ccache_to_list: " "added ccache [%s] for user [%s] to the list\n", ccname, username)); if (entry->event) { /* * If we're set up to renew our krb5 tickets, we must * cache the credentials in memory for the ticket * renew function. Fix inspired by patch from * Ian Gordon <*****@*****.**> for * bugid #9098. */ ntret = winbindd_add_memory_creds(username, uid, pass); DEBUG(10, ("winbindd_add_memory_creds returned: %s\n", nt_errstr(ntret))); } return NT_STATUS_OK; no_mem: TALLOC_FREE(entry); return NT_STATUS_NO_MEMORY; }
static void krb5_ticket_gain_handler(struct tevent_context *event_ctx, struct tevent_timer *te, struct timeval now, void *private_data) { struct WINBINDD_CCACHE_ENTRY *entry = talloc_get_type_abort(private_data, struct WINBINDD_CCACHE_ENTRY); #ifdef HAVE_KRB5 int ret; struct timeval t; struct WINBINDD_MEMORY_CREDS *cred_ptr = entry->cred_ptr; struct winbindd_domain *domain = NULL; #endif DEBUG(10,("krb5_ticket_gain_handler called\n")); DEBUGADD(10,("event called for: %s, %s\n", entry->ccname, entry->username)); TALLOC_FREE(entry->event); #ifdef HAVE_KRB5 if (!cred_ptr || !cred_ptr->pass) { DEBUG(10,("krb5_ticket_gain_handler: no memory creds\n")); return; } if ((domain = find_domain_from_name(entry->realm)) == NULL) { DEBUG(0,("krb5_ticket_gain_handler: unknown domain\n")); return; } if (!domain->online) { goto retry_later; } set_effective_uid(entry->uid); ret = kerberos_kinit_password_ext(entry->principal_name, cred_ptr->pass, 0, /* hm, can we do time correction here ? */ &entry->refresh_time, &entry->renew_until, entry->ccname, False, /* no PAC required anymore */ True, WINBINDD_PAM_AUTH_KRB5_RENEW_TIME, NULL); gain_root_privilege(); if (ret) { DEBUG(3,("krb5_ticket_gain_handler: " "could not kinit: %s\n", error_message(ret))); /* evil. If we cannot do it, destroy any the __maybe__ * __existing__ ticket */ ads_kdestroy(entry->ccname); goto retry_later; } DEBUG(10,("krb5_ticket_gain_handler: " "successful kinit for: %s in ccache: %s\n", entry->principal_name, entry->ccname)); goto got_ticket; retry_later: #if defined(DEBUG_KRB5_TKT_RENEWAL) t = timeval_set(time(NULL) + 30, 0); #else t = timeval_current_ofs(MAX(30, lp_winbind_cache_time()), 0); #endif add_krb5_ticket_gain_handler_event(entry, t); return; got_ticket: #if defined(DEBUG_KRB5_TKT_RENEWAL) t = timeval_set(time(NULL) + 30, 0); #else t = timeval_set(krb5_event_refresh_time(entry->refresh_time), 0); #endif if (entry->refresh_time == 0) { entry->refresh_time = t.tv_sec; } entry->event = tevent_add_timer(winbind_event_context(), entry, t, krb5_ticket_refresh_handler, entry); return; #endif }
static void krb5_ticket_refresh_handler(struct tevent_context *event_ctx, struct tevent_timer *te, struct timeval now, void *private_data) { struct WINBINDD_CCACHE_ENTRY *entry = talloc_get_type_abort(private_data, struct WINBINDD_CCACHE_ENTRY); #ifdef HAVE_KRB5 int ret; time_t new_start; time_t expire_time = 0; struct WINBINDD_MEMORY_CREDS *cred_ptr = entry->cred_ptr; #endif DEBUG(10,("krb5_ticket_refresh_handler called\n")); DEBUGADD(10,("event called for: %s, %s\n", entry->ccname, entry->username)); TALLOC_FREE(entry->event); #ifdef HAVE_KRB5 /* Kinit again if we have the user password and we can't renew the old * tgt anymore * NB * This happens when machine are put to sleep for a very long time. */ if (entry->renew_until < time(NULL)) { rekinit: if (cred_ptr && cred_ptr->pass) { set_effective_uid(entry->uid); ret = kerberos_kinit_password_ext(entry->principal_name, cred_ptr->pass, 0, /* hm, can we do time correction here ? */ &entry->refresh_time, &entry->renew_until, entry->ccname, False, /* no PAC required anymore */ True, WINBINDD_PAM_AUTH_KRB5_RENEW_TIME, NULL); gain_root_privilege(); if (ret) { DEBUG(3,("krb5_ticket_refresh_handler: " "could not re-kinit: %s\n", error_message(ret))); /* destroy the ticket because we cannot rekinit * it, ignore error here */ ads_kdestroy(entry->ccname); /* Don't break the ticket refresh chain: retry * refreshing ticket sometime later when KDC is * unreachable -- BoYang. More error code handling * here? * */ if ((ret == KRB5_KDC_UNREACH) || (ret == KRB5_REALM_CANT_RESOLVE)) { #if defined(DEBUG_KRB5_TKT_RENEWAL) new_start = time(NULL) + 30; #else new_start = time(NULL) + MAX(30, lp_winbind_cache_time()); #endif add_krb5_ticket_gain_handler_event(entry, timeval_set(new_start, 0)); return; } TALLOC_FREE(entry->event); return; } DEBUG(10,("krb5_ticket_refresh_handler: successful re-kinit " "for: %s in ccache: %s\n", entry->principal_name, entry->ccname)); #if defined(DEBUG_KRB5_TKT_RENEWAL) new_start = time(NULL) + 30; #else /* The tkt should be refreshed at one-half the period from now to the expiration time */ expire_time = entry->refresh_time; new_start = krb5_event_refresh_time(entry->refresh_time); #endif goto done; } else { /* can this happen? * No cached credentials * destroy ticket and refresh chain * */ ads_kdestroy(entry->ccname); TALLOC_FREE(entry->event); return; } } set_effective_uid(entry->uid); ret = smb_krb5_renew_ticket(entry->ccname, entry->principal_name, entry->service, &new_start); #if defined(DEBUG_KRB5_TKT_RENEWAL) new_start = time(NULL) + 30; #else expire_time = new_start; new_start = krb5_event_refresh_time(new_start); #endif gain_root_privilege(); if (ret) { DEBUG(3,("krb5_ticket_refresh_handler: " "could not renew tickets: %s\n", error_message(ret))); /* maybe we are beyond the renewing window */ /* evil rises here, we refresh ticket failed, * but the ticket might be expired. Therefore, * When we refresh ticket failed, destory the * ticket */ ads_kdestroy(entry->ccname); /* avoid breaking the renewal chain: retry in * lp_winbind_cache_time() seconds when the KDC was not * available right now. * the return code can be KRB5_REALM_CANT_RESOLVE. * More error code handling here? */ if ((ret == KRB5_KDC_UNREACH) || (ret == KRB5_REALM_CANT_RESOLVE)) { #if defined(DEBUG_KRB5_TKT_RENEWAL) new_start = time(NULL) + 30; #else new_start = time(NULL) + MAX(30, lp_winbind_cache_time()); #endif /* ticket is destroyed here, we have to regain it * if it is possible */ add_krb5_ticket_gain_handler_event(entry, timeval_set(new_start, 0)); return; } /* This is evil, if the ticket was already expired. * renew ticket function returns KRB5KRB_AP_ERR_TKT_EXPIRED. * But there is still a chance that we can rekinit it. * * This happens when user login in online mode, and then network * down or something cause winbind goes offline for a very long time, * and then goes online again. ticket expired, renew failed. * This happens when machine are put to sleep for a long time, * but shorter than entry->renew_util. * NB * Looks like the KDC is reachable, we want to rekinit as soon as * possible instead of waiting some time later. */ if ((ret == KRB5KRB_AP_ERR_TKT_EXPIRED) || (ret == KRB5_FCC_NOFILE)) goto rekinit; return; } done: /* in cases that ticket will be unrenewable soon, we don't try to renew ticket * but try to regain ticket if it is possible */ if (entry->renew_until && expire_time && (entry->renew_until <= expire_time)) { /* try to regain ticket 10 seconds before expiration */ expire_time -= 10; add_krb5_ticket_gain_handler_event(entry, timeval_set(expire_time, 0)); return; } if (entry->refresh_time == 0) { entry->refresh_time = new_start; } entry->event = tevent_add_timer(winbind_event_context(), entry, timeval_set(new_start, 0), krb5_ticket_refresh_handler, entry); #endif }
WERROR nt_printer_publish(TALLOC_CTX *mem_ctx, const struct auth_session_info *session_info, struct messaging_context *msg_ctx, struct spoolss_PrinterInfo2 *pinfo2, int action) { uint32_t info2_mask = SPOOLSS_PRINTER_INFO_ATTRIBUTES; struct spoolss_SetPrinterInfo2 *sinfo2; ADS_STATUS ads_rc; ADS_STRUCT *ads = NULL; WERROR win_rc; char *old_krb5ccname = NULL; sinfo2 = talloc_zero(mem_ctx, struct spoolss_SetPrinterInfo2); if (!sinfo2) { return WERR_NOMEM; } switch (action) { case DSPRINT_PUBLISH: case DSPRINT_UPDATE: pinfo2->attributes |= PRINTER_ATTRIBUTE_PUBLISHED; break; case DSPRINT_UNPUBLISH: pinfo2->attributes &= (~PRINTER_ATTRIBUTE_PUBLISHED); break; default: win_rc = WERR_NOT_SUPPORTED; goto done; } sinfo2->attributes = pinfo2->attributes; win_rc = winreg_update_printer_internal(mem_ctx, session_info, msg_ctx, pinfo2->sharename, info2_mask, sinfo2, NULL, NULL); if (!W_ERROR_IS_OK(win_rc)) { DEBUG(3, ("err %d saving data\n", W_ERROR_V(win_rc))); goto done; } TALLOC_FREE(sinfo2); ads = ads_init(lp_realm(), lp_workgroup(), NULL); if (!ads) { DEBUG(3, ("ads_init() failed\n")); win_rc = WERR_SERVER_UNAVAILABLE; goto done; } old_krb5ccname = getenv(KRB5_ENV_CCNAME); setenv(KRB5_ENV_CCNAME, "MEMORY:prtpub_cache", 1); SAFE_FREE(ads->auth.password); ads->auth.password = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL); /* ads_connect() will find the DC for us */ ads_rc = ads_connect(ads); if (!ADS_ERR_OK(ads_rc)) { DEBUG(3, ("ads_connect failed: %s\n", ads_errstr(ads_rc))); win_rc = WERR_ACCESS_DENIED; goto done; } switch (action) { case DSPRINT_PUBLISH: case DSPRINT_UPDATE: win_rc = nt_printer_publish_ads(msg_ctx, ads, pinfo2); break; case DSPRINT_UNPUBLISH: win_rc = nt_printer_unpublish_ads(ads, pinfo2->sharename); break; } done: ads_destroy(&ads); ads_kdestroy("MEMORY:prtpub_cache"); unsetenv(KRB5_ENV_CCNAME); if (old_krb5ccname) { setenv(KRB5_ENV_CCNAME, old_krb5ccname, 0); } return win_rc; }
/* * Given the username/password, do a kinit, store the ticket in * cache_name if specified, and return the PAC_LOGON_INFO (the * structure containing the important user information such as * groups). */ NTSTATUS kerberos_return_pac(TALLOC_CTX *mem_ctx, const char *name, const char *pass, time_t time_offset, time_t *expire_time, time_t *renew_till_time, const char *cache_name, bool request_pac, bool add_netbios_addr, time_t renewable_time, const char *impersonate_princ_s, struct PAC_LOGON_INFO **_logon_info) { krb5_error_code ret; NTSTATUS status = NT_STATUS_INVALID_PARAMETER; DATA_BLOB tkt, tkt_wrapped, ap_rep, sesskey1; const char *auth_princ = NULL; const char *local_service = NULL; const char *cc = "MEMORY:kerberos_return_pac"; struct auth_session_info *session_info; struct gensec_security *gensec_server_context; struct gensec_settings *gensec_settings; size_t idx = 0; struct auth4_context *auth_context; struct loadparm_context *lp_ctx; struct PAC_LOGON_INFO *logon_info = NULL; TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); NT_STATUS_HAVE_NO_MEMORY(tmp_ctx); ZERO_STRUCT(tkt); ZERO_STRUCT(ap_rep); ZERO_STRUCT(sesskey1); if (!name || !pass) { return NT_STATUS_INVALID_PARAMETER; } if (cache_name) { cc = cache_name; } if (!strchr_m(name, '@')) { auth_princ = talloc_asprintf(mem_ctx, "%s@%s", name, lp_realm()); } else { auth_princ = name; } NT_STATUS_HAVE_NO_MEMORY(auth_princ); local_service = talloc_asprintf(mem_ctx, "%s$@%s", lp_netbios_name(), lp_realm()); NT_STATUS_HAVE_NO_MEMORY(local_service); ret = kerberos_kinit_password_ext(auth_princ, pass, time_offset, expire_time, renew_till_time, cc, request_pac, add_netbios_addr, renewable_time, &status); if (ret) { DEBUG(1,("kinit failed for '%s' with: %s (%d)\n", auth_princ, error_message(ret), ret)); /* status already set */ goto out; } DEBUG(10,("got TGT for %s in %s\n", auth_princ, cc)); if (expire_time) { DEBUGADD(10,("\tvalid until: %s (%d)\n", http_timestring(talloc_tos(), *expire_time), (int)*expire_time)); } if (renew_till_time) { DEBUGADD(10,("\trenewable till: %s (%d)\n", http_timestring(talloc_tos(), *renew_till_time), (int)*renew_till_time)); } /* we cannot continue with krb5 when UF_DONT_REQUIRE_PREAUTH is set, * in that case fallback to NTLM - gd */ if (expire_time && renew_till_time && (*expire_time == 0) && (*renew_till_time == 0)) { return NT_STATUS_INVALID_LOGON_TYPE; } ret = cli_krb5_get_ticket(mem_ctx, local_service, time_offset, &tkt, &sesskey1, 0, cc, NULL, impersonate_princ_s); if (ret) { DEBUG(1,("failed to get ticket for %s: %s\n", local_service, error_message(ret))); if (impersonate_princ_s) { DEBUGADD(1,("tried S4U2SELF impersonation as: %s\n", impersonate_princ_s)); } status = krb5_to_nt_status(ret); goto out; } /* wrap that up in a nice GSS-API wrapping */ tkt_wrapped = spnego_gen_krb5_wrap(tmp_ctx, tkt, TOK_ID_KRB_AP_REQ); if (tkt_wrapped.data == NULL) { status = NT_STATUS_NO_MEMORY; goto out; } auth_context = talloc_zero(tmp_ctx, struct auth4_context); if (auth_context == NULL) { status = NT_STATUS_NO_MEMORY; goto out; } auth_context->generate_session_info_pac = kerberos_fetch_pac; lp_ctx = loadparm_init_s3(tmp_ctx, loadparm_s3_context()); if (lp_ctx == NULL) { status = NT_STATUS_INVALID_SERVER_STATE; DEBUG(10, ("loadparm_init_s3 failed\n")); goto out; } gensec_settings = lpcfg_gensec_settings(tmp_ctx, lp_ctx); if (lp_ctx == NULL) { status = NT_STATUS_NO_MEMORY; DEBUG(10, ("lpcfg_gensec_settings failed\n")); goto out; } gensec_settings->backends = talloc_zero_array(gensec_settings, struct gensec_security_ops *, 2); if (gensec_settings->backends == NULL) { status = NT_STATUS_NO_MEMORY; goto out; } gensec_init(); gensec_settings->backends[idx++] = &gensec_gse_krb5_security_ops; status = gensec_server_start(tmp_ctx, gensec_settings, auth_context, &gensec_server_context); if (!NT_STATUS_IS_OK(status)) { DEBUG(1, (__location__ "Failed to start server-side GENSEC to validate a Kerberos ticket: %s\n", nt_errstr(status))); goto out; } talloc_unlink(tmp_ctx, lp_ctx); talloc_unlink(tmp_ctx, gensec_settings); talloc_unlink(tmp_ctx, auth_context); status = gensec_start_mech_by_oid(gensec_server_context, GENSEC_OID_KERBEROS5); if (!NT_STATUS_IS_OK(status)) { DEBUG(1, (__location__ "Failed to start server-side GENSEC krb5 to validate a Kerberos ticket: %s\n", nt_errstr(status))); goto out; } /* Do a client-server update dance */ status = gensec_update(gensec_server_context, tmp_ctx, NULL, tkt_wrapped, &ap_rep); if (!NT_STATUS_IS_OK(status)) { DEBUG(1, ("gensec_update() failed: %s\n", nt_errstr(status))); goto out; } /* Now return the PAC information to the callers. We ingore * the session_info and instead pick out the PAC via the * private_data on the auth_context */ status = gensec_session_info(gensec_server_context, tmp_ctx, &session_info); if (!NT_STATUS_IS_OK(status)) { DEBUG(1, ("Unable to obtain PAC via gensec_session_info\n")); goto out; } logon_info = talloc_get_type_abort(gensec_server_context->auth_context->private_data, struct PAC_LOGON_INFO); if (logon_info == NULL) { DEBUG(1,("no PAC\n")); status = NT_STATUS_INVALID_PARAMETER; goto out; } *_logon_info = talloc_move(mem_ctx, &logon_info); out: talloc_free(tmp_ctx); if (cc != cache_name) { ads_kdestroy(cc); } data_blob_free(&tkt); data_blob_free(&ap_rep); data_blob_free(&sesskey1); return status; }