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 krb5_error_code encode_524_response(krb5_context context, krb5_kdc_configuration *config, const char *spn, const EncTicketPart et, const Ticket *t, hdb_entry_ex *server, EncryptedData *ticket, int *kvno) { krb5_error_code ret; int use_2b; size_t len; use_2b = krb5_config_get_bool(context, NULL, "kdc", "use_2b", spn, NULL); if(use_2b) { ASN1_MALLOC_ENCODE(EncryptedData, ticket->cipher.data, ticket->cipher.length, &t->enc_part, &len, ret); if (ret) { kdc_log(context, config, 0, "Failed to encode v4 (2b) ticket (%s)", spn); return ret; } ticket->etype = 0; ticket->kvno = NULL; *kvno = 213; /* 2b's use this magic kvno */ } else { unsigned char buf[MAX_KTXT_LEN + 4 * 4]; Key *skey; if (!config->enable_v4_cross_realm && strcmp (et.crealm, t->realm) != 0) { kdc_log(context, config, 0, "524 cross-realm %s -> %s disabled", et.crealm, t->realm); return KRB5KDC_ERR_POLICY; } ret = _kdc_encode_v4_ticket(context, config, buf + sizeof(buf) - 1, sizeof(buf), &et, &t->sname, &len); if(ret){ kdc_log(context, config, 0, "Failed to encode v4 ticket (%s)", spn); return ret; } ret = _kdc_get_des_key(context, server, TRUE, FALSE, &skey); if(ret){ kdc_log(context, config, 0, "no suitable DES key for server (%s)", spn); return ret; } ret = encrypt_v4_ticket(context, config, buf + sizeof(buf) - len, len, &skey->key, ticket); if(ret){ kdc_log(context, config, 0, "Failed to encrypt v4 ticket (%s)", spn); return ret; } *kvno = server->entry.kvno; } return 0; }
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); }