static NTSTATUS connect_to_domain_password_server(struct cli_state **cli, const char *domain, const char *dc_name, struct in_addr dc_ip, const char *setup_creds_as, uint16 sec_chan, const unsigned char *trust_passwd, BOOL *retry) { NTSTATUS result; /* TODO: Send a SAMLOGON request to determine whether this is a valid logonserver. We can avoid a 30-second timeout if the DC is down if the SAMLOGON request fails as it is only over UDP. */ /* we use a mutex to prevent two connections at once - when a Win2k PDC get two connections where one hasn't completed a session setup yet it will send a TCP reset to the first connection (tridge) */ /* * With NT4.x DC's *all* authentication must be serialized to avoid * ACCESS_DENIED errors if 2 auths are done from the same machine. JRA. */ if (!grab_server_mutex(dc_name)) return NT_STATUS_NO_LOGON_SERVERS; /* Attempt connection */ *retry = True; result = cli_full_connection(cli, global_myname(), dc_name, &dc_ip, 0, "IPC$", "IPC", "", "", "", 0, Undefined, retry); if (!NT_STATUS_IS_OK(result)) { /* map to something more useful */ if (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)) { result = NT_STATUS_NO_LOGON_SERVERS; } release_server_mutex(); return result; } /* * We now have an anonymous connection to IPC$ on the domain password server. */ /* * Even if the connect succeeds we need to setup the netlogon * pipe here. We do this as we may just have changed the domain * account password on the PDC and yet we may be talking to * a BDC that doesn't have this replicated yet. In this case * a successful connect to a DC needs to take the netlogon connect * into account also. This patch from "Bjart Kvarme" <*****@*****.**>. */ if(cli_nt_session_open(*cli, PI_NETLOGON) == False) { DEBUG(0,("connect_to_domain_password_server: unable to open the domain client session to \ machine %s. Error was : %s.\n", dc_name, cli_errstr(*cli))); cli_nt_session_close(*cli); cli_ulogoff(*cli); cli_shutdown(*cli); release_server_mutex(); return NT_STATUS_NO_LOGON_SERVERS; }
static struct cli_state *server_cryptkey(TALLOC_CTX *mem_ctx) { struct cli_state *cli = NULL; fstring desthost; struct in_addr dest_ip; const char *p; char *pserver; BOOL connected_ok = False; if (!(cli = cli_initialise())) return NULL; /* security = server just can't function with spnego */ cli->use_spnego = False; pserver = talloc_strdup(mem_ctx, lp_passwordserver()); p = pserver; while(next_token( &p, desthost, LIST_SEP, sizeof(desthost))) { standard_sub_basic(current_user_info.smb_name, current_user_info.domain, desthost, sizeof(desthost)); strupper_m(desthost); if(!resolve_name( desthost, &dest_ip, 0x20)) { DEBUG(1,("server_cryptkey: Can't resolve address for %s\n",desthost)); continue; } if (ismyip(dest_ip)) { DEBUG(1,("Password server loop - disabling password server %s\n",desthost)); continue; } /* we use a mutex to prevent two connections at once - when a Win2k PDC get two connections where one hasn't completed a session setup yet it will send a TCP reset to the first connection (tridge) */ if (!grab_server_mutex(desthost)) { return NULL; } if (cli_connect(cli, desthost, &dest_ip)) { DEBUG(3,("connected to password server %s\n",desthost)); connected_ok = True; break; } } if (!connected_ok) { release_server_mutex(); DEBUG(0,("password server not available\n")); cli_shutdown(cli); return NULL; } if (!attempt_netbios_session_request(&cli, global_myname(), desthost, &dest_ip)) { release_server_mutex(); DEBUG(1,("password server fails session request\n")); cli_shutdown(cli); return NULL; } if (strequal(desthost,myhostname())) { exit_server_cleanly("Password server loop!"); } DEBUG(3,("got session\n")); if (!cli_negprot(cli)) { DEBUG(1,("%s rejected the negprot\n",desthost)); release_server_mutex(); cli_shutdown(cli); return NULL; } if (cli->protocol < PROTOCOL_LANMAN2 || !(cli->sec_mode & NEGOTIATE_SECURITY_USER_LEVEL)) { DEBUG(1,("%s isn't in user level security mode\n",desthost)); release_server_mutex(); cli_shutdown(cli); return NULL; } /* Get the first session setup done quickly, to avoid silly Win2k bugs. (The next connection to the server will kill this one... */ if (!NT_STATUS_IS_OK(cli_session_setup(cli, "", "", 0, "", 0, ""))) { DEBUG(0,("%s rejected the initial session setup (%s)\n", desthost, cli_errstr(cli))); release_server_mutex(); cli_shutdown(cli); return NULL; } release_server_mutex(); DEBUG(3,("password server OK\n")); return cli; }
NTSTATUS ads_verify_ticket(TALLOC_CTX *mem_ctx, const char *realm, time_t time_offset, const DATA_BLOB *ticket, char **principal, PAC_DATA **pac_data, DATA_BLOB *ap_rep, DATA_BLOB *session_key) { NTSTATUS sret = NT_STATUS_LOGON_FAILURE; NTSTATUS pac_ret; DATA_BLOB auth_data; krb5_context context = NULL; krb5_auth_context auth_context = NULL; krb5_data packet; krb5_ticket *tkt = NULL; krb5_rcache rcache = NULL; krb5_keyblock *keyblock = NULL; time_t authtime; krb5_error_code ret = 0; krb5_principal host_princ = NULL; krb5_const_principal client_principal = NULL; char *host_princ_s = NULL; BOOL auth_ok = False; BOOL got_replay_mutex = False; BOOL got_auth_data = False; ZERO_STRUCT(packet); ZERO_STRUCT(auth_data); *principal = NULL; *pac_data = NULL; *ap_rep = data_blob(NULL,0); *session_key = data_blob(NULL,0); initialize_krb5_error_table(); ret = krb5_init_context(&context); if (ret) { DEBUG(1,("ads_verify_ticket: krb5_init_context failed (%s)\n", error_message(ret))); return NT_STATUS_LOGON_FAILURE; } if (time_offset != 0) { krb5_set_real_time(context, time(NULL) + time_offset, 0); } ret = krb5_set_default_realm(context, realm); if (ret) { DEBUG(1,("ads_verify_ticket: krb5_set_default_realm failed (%s)\n", error_message(ret))); goto out; } /* This whole process is far more complex than I would like. We have to go through all this to allow us to store the secret internally, instead of using /etc/krb5.keytab */ ret = krb5_auth_con_init(context, &auth_context); if (ret) { DEBUG(1,("ads_verify_ticket: krb5_auth_con_init failed (%s)\n", error_message(ret))); goto out; } asprintf(&host_princ_s, "%s$", global_myname()); if (!host_princ_s) { goto out; } strlower_m(host_princ_s); ret = smb_krb5_parse_name(context, host_princ_s, &host_princ); if (ret) { DEBUG(1,("ads_verify_ticket: smb_krb5_parse_name(%s) failed (%s)\n", host_princ_s, error_message(ret))); goto out; } /* Lock a mutex surrounding the replay as there is no locking in the MIT krb5 * code surrounding the replay cache... */ if (!grab_server_mutex("replay cache mutex")) { DEBUG(1,("ads_verify_ticket: unable to protect replay cache with mutex.\n")); ret = KRB5_CC_IO; goto out; } got_replay_mutex = True; /* * JRA. We must set the rcache here. This will prevent replay attacks. */ ret = krb5_get_server_rcache(context, krb5_princ_component(context, host_princ, 0), &rcache); if (ret) { DEBUG(1,("ads_verify_ticket: krb5_get_server_rcache failed (%s)\n", error_message(ret))); goto out; } ret = krb5_auth_con_setrcache(context, auth_context, rcache); if (ret) { DEBUG(1,("ads_verify_ticket: krb5_auth_con_setrcache failed (%s)\n", error_message(ret))); goto out; } if (lp_use_kerberos_keytab()) { auth_ok = ads_keytab_verify_ticket(context, auth_context, ticket, &tkt, &keyblock, &ret); } if (!auth_ok) { auth_ok = ads_secrets_verify_ticket(context, auth_context, host_princ, ticket, &tkt, &keyblock, &ret); } release_server_mutex(); got_replay_mutex = False; #if 0 /* Heimdal leaks here, if we fix the leak, MIT crashes */ if (rcache) { krb5_rc_close(context, rcache); } #endif if (!auth_ok) { DEBUG(3,("ads_verify_ticket: krb5_rd_req with auth failed (%s)\n", error_message(ret))); /* Try map the error return in case it's something like * a clock skew error. */ sret = krb5_to_nt_status(ret); if (NT_STATUS_IS_OK(sret) || NT_STATUS_EQUAL(sret,NT_STATUS_UNSUCCESSFUL)) { sret = NT_STATUS_LOGON_FAILURE; } DEBUG(10,("ads_verify_ticket: returning error %s\n", nt_errstr(sret) )); goto out; } authtime = get_authtime_from_tkt(tkt); client_principal = get_principal_from_tkt(tkt); ret = krb5_mk_rep(context, auth_context, &packet); if (ret) { DEBUG(3,("ads_verify_ticket: Failed to generate mutual authentication reply (%s)\n", error_message(ret))); goto out; } *ap_rep = data_blob(packet.data, packet.length); if (packet.data) { kerberos_free_data_contents(context, &packet); ZERO_STRUCT(packet); } get_krb5_smb_session_key(context, auth_context, session_key, True); dump_data_pw("SMB session key (from ticket)\n", session_key->data, session_key->length); #if 0 file_save("/tmp/ticket.dat", ticket->data, ticket->length); #endif /* continue when no PAC is retrieved or we couldn't decode the PAC (like accounts that have the UF_NO_AUTH_DATA_REQUIRED flag set, or Kerberos tickets encrypted using a DES key) - Guenther */ got_auth_data = get_auth_data_from_tkt(mem_ctx, &auth_data, tkt); if (!got_auth_data) { DEBUG(3,("ads_verify_ticket: did not retrieve auth data. continuing without PAC\n")); } if (got_auth_data && pac_data != NULL) { pac_ret = decode_pac_data(mem_ctx, &auth_data, context, keyblock, client_principal, authtime, pac_data); if (!NT_STATUS_IS_OK(pac_ret)) { DEBUG(3,("ads_verify_ticket: failed to decode PAC_DATA: %s\n", nt_errstr(pac_ret))); *pac_data = NULL; } data_blob_free(&auth_data); } #if 0 #if defined(HAVE_KRB5_TKT_ENC_PART2) /* MIT */ if (tkt->enc_part2) { file_save("/tmp/authdata.dat", tkt->enc_part2->authorization_data[0]->contents, tkt->enc_part2->authorization_data[0]->length); } #else /* Heimdal */ if (tkt->ticket.authorization_data) { file_save("/tmp/authdata.dat", tkt->ticket.authorization_data->val->ad_data.data, tkt->ticket.authorization_data->val->ad_data.length); } #endif #endif if ((ret = smb_krb5_unparse_name(context, client_principal, principal))) { DEBUG(3,("ads_verify_ticket: smb_krb5_unparse_name failed (%s)\n", error_message(ret))); sret = NT_STATUS_LOGON_FAILURE; goto out; } sret = NT_STATUS_OK; out: if (got_replay_mutex) { release_server_mutex(); } if (!NT_STATUS_IS_OK(sret)) { data_blob_free(&auth_data); } if (!NT_STATUS_IS_OK(sret)) { data_blob_free(ap_rep); } if (host_princ) { krb5_free_principal(context, host_princ); } if (keyblock) { krb5_free_keyblock(context, keyblock); } if (tkt != NULL) { krb5_free_ticket(context, tkt); } SAFE_FREE(host_princ_s); if (auth_context) { krb5_auth_con_free(context, auth_context); } if (context) { krb5_free_context(context); } return sret; }