static krb5_error_code samba_wdc_check_client_access(void *priv, krb5_context context, krb5_kdc_configuration *config, hdb_entry_ex *client_ex, const char *client_name, hdb_entry_ex *server_ex, const char *server_name, KDC_REQ *req, krb5_data *e_data) { struct samba_kdc_entry *kdc_entry; bool password_change; char *workstation; NTSTATUS nt_status; kdc_entry = talloc_get_type(client_ex->ctx, struct samba_kdc_entry); password_change = (server_ex && server_ex->entry.flags.change_pw); workstation = get_netbios_name((TALLOC_CTX *)client_ex->ctx, req->req_body.addresses); nt_status = samba_kdc_check_client_access(kdc_entry, client_name, workstation, password_change); if (!NT_STATUS_IS_OK(nt_status)) { if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_MEMORY)) { return ENOMEM; } if (e_data) { DATA_BLOB data; samba_kdc_build_edata_reply(nt_status, &data); *e_data = fill_krb5_data(data.data, data.length); } return samba_kdc_map_policy_err(nt_status); } /* Now do the standard Heimdal check */ return kdc_check_flags(context, config, client_ex, client_name, server_ex, server_name, req->msg_type == krb_as_req); }
krb5_error_code _kdc_check_access(krb5_context context, krb5_kdc_configuration *config, hdb_entry_ex *client_ex, const char *client_name, hdb_entry_ex *server_ex, const char *server_name, KDC_REQ *req, krb5_data *e_data) { if (windcft == NULL) return kdc_check_flags(context, config, client_ex, client_name, server_ex, server_name, req->msg_type == krb_as_req); return (windcft->client_access)(windcctx, context, config, client_ex, client_name, server_ex, server_name, req, e_data); }
static void do_getticket (krb5_context context, krb5_kdc_configuration *config, struct rx_header *hdr, krb5_storage *sp, struct sockaddr_in *addr, const char *from, krb5_data *reply) { krb5_error_code ret; int kvno; char *auth_domain = NULL; krb5_data aticket; char *name = NULL; char *instance = NULL; krb5_data times; int32_t max_seq_len; hdb_entry_ex *server_entry = NULL; hdb_entry_ex *client_entry = NULL; hdb_entry_ex *krbtgt_entry = NULL; Key *kkey = NULL; Key *skey = NULL; DES_cblock key; DES_key_schedule schedule; DES_cblock session; time_t max_life; int8_t life; time_t start_time, end_time; char server_name[256]; char client_name[256]; struct _krb5_krb_auth_data ad; krb5_data_zero (&aticket); krb5_data_zero (×); memset(&ad, 0, sizeof(ad)); unparse_getticket_args (sp, &kvno, &auth_domain, &aticket, &name, &instance, ×, &max_seq_len); if (times.length < 8) { make_error_reply (hdr, KABADREQUEST, reply); goto out; } snprintf (server_name, sizeof(server_name), "%s.%s@%s", name, instance, config->v4_realm); ret = _kdc_db_fetch4 (context, config, name, instance, config->v4_realm, HDB_F_GET_SERVER, &server_entry); if (ret) { kdc_log(context, config, 0, "Server not found in database: %s: %s", server_name, krb5_get_err_text(context, ret)); make_error_reply (hdr, KANOENT, reply); goto out; } ret = _kdc_db_fetch4 (context, config, "krbtgt", config->v4_realm, config->v4_realm, HDB_F_GET_KRBTGT, &krbtgt_entry); if (ret) { kdc_log(context, config, 0, "Server not found in database: %s.%s@%s: %s", "krbtgt", config->v4_realm, config->v4_realm, krb5_get_err_text(context, ret)); make_error_reply (hdr, KANOENT, reply); goto out; } /* find a DES key */ ret = _kdc_get_des_key(context, krbtgt_entry, TRUE, TRUE, &kkey); if(ret){ kdc_log(context, config, 0, "no suitable DES key for krbtgt"); make_error_reply (hdr, KANOKEYS, reply); goto out; } /* find a DES key */ ret = _kdc_get_des_key(context, server_entry, TRUE, TRUE, &skey); if(ret){ kdc_log(context, config, 0, "no suitable DES key for server"); make_error_reply (hdr, KANOKEYS, reply); goto out; } /* decrypt the incoming ticket */ memcpy (&key, kkey->key.keyvalue.data, sizeof(key)); /* unpack the ticket */ { char *sname = NULL; char *sinstance = NULL; ret = _krb5_krb_decomp_ticket(context, &aticket, &kkey->key, config->v4_realm, &sname, &sinstance, &ad); if (ret) { const char *msg = krb5_get_error_message(context, ret); kdc_log(context, config, 0, "kaserver: decomp failed for %s.%s with %s %d", msg, sname, sinstance, ret); krb5_free_error_message(context, msg); make_error_reply (hdr, KABADTICKET, reply); goto out; } if (strcmp (sname, "krbtgt") != 0 || strcmp (sinstance, config->v4_realm) != 0) { kdc_log(context, config, 0, "no TGT: %s.%s for %s.%s@%s", sname, sinstance, ad.pname, ad.pinst, ad.prealm); make_error_reply (hdr, KABADTICKET, reply); free(sname); free(sinstance); goto out; } free(sname); free(sinstance); if (kdc_time > _krb5_krb_life_to_time(ad.time_sec, ad.life)) { kdc_log(context, config, 0, "TGT expired: %s.%s@%s", ad.pname, ad.pinst, ad.prealm); make_error_reply (hdr, KABADTICKET, reply); goto out; } } snprintf (client_name, sizeof(client_name), "%s.%s@%s", ad.pname, ad.pinst, ad.prealm); kdc_log(context, config, 0, "TGS-REQ (kaserver) %s from %s for %s", client_name, from, server_name); ret = _kdc_db_fetch4 (context, config, ad.pname, ad.pinst, ad.prealm, HDB_F_GET_CLIENT, &client_entry); if(ret && ret != HDB_ERR_NOENTRY) { kdc_log(context, config, 0, "Client not found in database: (krb4) %s: %s", client_name, krb5_get_err_text(context, ret)); make_error_reply (hdr, KANOENT, reply); goto out; } if (client_entry == NULL && strcmp(ad.prealm, config->v4_realm) == 0) { kdc_log(context, config, 0, "Local client not found in database: (krb4) " "%s", client_name); make_error_reply (hdr, KANOENT, reply); goto out; } ret = kdc_check_flags (context, config, client_entry, client_name, server_entry, server_name, FALSE); if (ret) { make_error_reply (hdr, KAPWEXPIRED, reply); goto out; } /* decrypt the times */ memcpy(&session, ad.session.keyvalue.data, sizeof(session)); DES_set_key_unchecked (&session, &schedule); DES_ecb_encrypt (times.data, times.data, &schedule, DES_DECRYPT); memset (&schedule, 0, sizeof(schedule)); memset (&session, 0, sizeof(session)); /* and extract them */ { krb5_storage *tsp; int32_t tmp; tsp = krb5_storage_from_mem (times.data, times.length); krb5_ret_int32 (tsp, &tmp); start_time = tmp; krb5_ret_int32 (tsp, &tmp); end_time = tmp; krb5_storage_free (tsp); } /* life */ max_life = end_time - kdc_time; /* end_time - kdc_time can sometimes be non-positive due to slight time skew between client and server. Let's make sure it is postive */ if(max_life < 1) max_life = 1; if (krbtgt_entry->entry.max_life) max_life = min(max_life, *krbtgt_entry->entry.max_life); if (server_entry->entry.max_life) max_life = min(max_life, *server_entry->entry.max_life); /* if this is a cross realm request, the client_entry will likely be NULL */ if (client_entry && client_entry->entry.max_life) max_life = min(max_life, *client_entry->entry.max_life); life = _krb5_krb_time_to_life(kdc_time, kdc_time + max_life); create_reply_ticket (context, hdr, skey, ad.pname, ad.pinst, ad.prealm, addr, life, server_entry->entry.kvno, max_seq_len, name, instance, 0, "gtkt", &ad.session, reply); out: _krb5_krb_free_auth_data(context, &ad); if (aticket.length) { memset (aticket.data, 0, aticket.length); krb5_data_free (&aticket); } if (times.length) { memset (times.data, 0, times.length); krb5_data_free (×); } if (auth_domain) free (auth_domain); if (name) free (name); if (instance) free (instance); if (krbtgt_entry) _kdc_free_ent (context, krbtgt_entry); if (server_entry) _kdc_free_ent (context, server_entry); }
static void do_authenticate (krb5_context context, krb5_kdc_configuration *config, struct rx_header *hdr, krb5_storage *sp, struct sockaddr_in *addr, const char *from, krb5_data *reply) { krb5_error_code ret; char *name = NULL; char *instance = NULL; time_t start_time; time_t end_time; krb5_data request; int32_t max_seq_len; hdb_entry_ex *client_entry = NULL; hdb_entry_ex *server_entry = NULL; Key *ckey = NULL; Key *skey = NULL; krb5_storage *reply_sp; time_t max_life; uint8_t life; int32_t chal; char client_name[256]; char server_name[256]; krb5_data_zero (&request); ret = unparse_auth_args (sp, &name, &instance, &start_time, &end_time, &request, &max_seq_len); if (ret != 0 || request.length < 8) { make_error_reply (hdr, KABADREQUEST, reply); goto out; } snprintf (client_name, sizeof(client_name), "%s.%s@%s", name, instance, config->v4_realm); snprintf (server_name, sizeof(server_name), "%s.%s@%s", "krbtgt", config->v4_realm, config->v4_realm); kdc_log(context, config, 0, "AS-REQ (kaserver) %s from %s for %s", client_name, from, server_name); ret = _kdc_db_fetch4 (context, config, name, instance, config->v4_realm, HDB_F_GET_CLIENT, &client_entry); if (ret) { kdc_log(context, config, 0, "Client not found in database: %s: %s", client_name, krb5_get_err_text(context, ret)); make_error_reply (hdr, KANOENT, reply); goto out; } ret = _kdc_db_fetch4 (context, config, "krbtgt", config->v4_realm, config->v4_realm, HDB_F_GET_KRBTGT, &server_entry); if (ret) { kdc_log(context, config, 0, "Server not found in database: %s: %s", server_name, krb5_get_err_text(context, ret)); make_error_reply (hdr, KANOENT, reply); goto out; } ret = kdc_check_flags (context, config, client_entry, client_name, server_entry, server_name, TRUE); if (ret) { make_error_reply (hdr, KAPWEXPIRED, reply); goto out; } /* find a DES key */ ret = _kdc_get_des_key(context, client_entry, FALSE, TRUE, &ckey); if(ret){ kdc_log(context, config, 0, "no suitable DES key for client"); make_error_reply (hdr, KANOKEYS, reply); goto out; } /* find a DES key */ ret = _kdc_get_des_key(context, server_entry, TRUE, TRUE, &skey); if(ret){ kdc_log(context, config, 0, "no suitable DES key for server"); make_error_reply (hdr, KANOKEYS, reply); goto out; } { DES_cblock key; DES_key_schedule schedule; /* try to decode the `request' */ memcpy (&key, ckey->key.keyvalue.data, sizeof(key)); DES_set_key_unchecked (&key, &schedule); DES_pcbc_encrypt (request.data, request.data, request.length, &schedule, &key, DES_DECRYPT); memset (&schedule, 0, sizeof(schedule)); memset (&key, 0, sizeof(key)); } /* check for the magic label */ if (memcmp ((char *)request.data + 4, "gTGS", 4) != 0) { kdc_log(context, config, 0, "preauth failed for %s", client_name); make_error_reply (hdr, KABADREQUEST, reply); goto out; } reply_sp = krb5_storage_from_mem (request.data, 4); krb5_ret_int32 (reply_sp, &chal); krb5_storage_free (reply_sp); if (abs(chal - kdc_time) > context->max_skew) { make_error_reply (hdr, KACLOCKSKEW, reply); goto out; } /* life */ max_life = end_time - kdc_time; /* end_time - kdc_time can sometimes be non-positive due to slight time skew between client and server. Let's make sure it is postive */ if(max_life < 1) max_life = 1; if (client_entry->entry.max_life) max_life = min(max_life, *client_entry->entry.max_life); if (server_entry->entry.max_life) max_life = min(max_life, *server_entry->entry.max_life); life = krb_time_to_life(kdc_time, kdc_time + max_life); create_reply_ticket (context, hdr, skey, name, instance, config->v4_realm, addr, life, server_entry->entry.kvno, max_seq_len, "krbtgt", config->v4_realm, chal + 1, "tgsT", &ckey->key, reply); out: if (request.length) { memset (request.data, 0, request.length); krb5_data_free (&request); } if (name) free (name); if (instance) free (instance); if (client_entry) _kdc_free_ent (context, client_entry); if (server_entry) _kdc_free_ent (context, server_entry); }