krb5_error_code KRB5_LIB_FUNCTION _krb5_krb_rd_req(krb5_context context, krb5_data *authent, const char *service, const char *instance, const char *local_realm, int32_t from_addr, const krb5_keyblock *key, struct _krb5_krb_auth_data *ad) { krb5_error_code ret; krb5_storage *sp; krb5_data ticket, eaut, aut; krb5_ssize_t size; int little_endian; int8_t pvno; int8_t type; int8_t s_kvno; uint8_t ticket_length; uint8_t eaut_length; uint8_t time_5ms; char *realm = NULL; char *sname = NULL; char *sinstance = NULL; char *r_realm = NULL; char *r_name = NULL; char *r_instance = NULL; uint32_t r_time_sec; /* Coarse time from authenticator */ unsigned long delta_t; /* Time in authenticator - local time */ long tkt_age; /* Age of ticket */ struct timeval tv; krb5_data_zero(&ticket); krb5_data_zero(&eaut); krb5_data_zero(&aut); sp = krb5_storage_from_data(authent); if (sp == NULL) { krb5_set_error_string(context, "alloc: out of memory"); return ENOMEM; } krb5_storage_set_eof_code(sp, EINVAL); /* XXX */ ret = krb5_ret_int8(sp, &pvno); if (ret) goto error; if (pvno != KRB_PROT_VERSION) { ret = EINVAL; /* XXX */ goto error; } ret = krb5_ret_int8(sp, &type); if (ret) goto error; little_endian = type & 1; type &= ~1; if(type != AUTH_MSG_APPL_REQUEST && type != AUTH_MSG_APPL_REQUEST_MUTUAL) { ret = EINVAL; /* RD_AP_MSG_TYPE */ goto error; } RCHECK(ret, krb5_ret_int8(sp, &s_kvno), error); RCHECK(ret, get_v4_stringz(sp, &realm, REALM_SZ), error); RCHECK(ret, krb5_ret_uint8(sp, &ticket_length), error); RCHECK(ret, krb5_ret_uint8(sp, &eaut_length), error); RCHECK(ret, krb5_data_alloc(&ticket, ticket_length), error); size = krb5_storage_read(sp, ticket.data, ticket.length); if (size != ticket.length) { ret = EINVAL; goto error; } /* Decrypt and take apart ticket */ ret = _krb5_krb_decomp_ticket(context, &ticket, key, local_realm, &sname, &sinstance, ad); if (ret) goto error; RCHECK(ret, krb5_data_alloc(&eaut, eaut_length), error); size = krb5_storage_read(sp, eaut.data, eaut.length); if (size != eaut.length) { ret = EINVAL; goto error; } krb5_storage_free(sp); sp = NULL; ret = decrypt_etext(context, &ad->session, &eaut, &aut); if (ret) goto error; sp = krb5_storage_from_data(&aut); if (sp == NULL) { krb5_set_error_string(context, "alloc: out of memory"); ret = ENOMEM; goto error; } if (little_endian) krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_LE); else krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_BE); RCHECK(ret, get_v4_stringz(sp, &r_name, ANAME_SZ), error); RCHECK(ret, get_v4_stringz(sp, &r_instance, INST_SZ), error); RCHECK(ret, get_v4_stringz(sp, &r_realm, REALM_SZ), error); RCHECK(ret, krb5_ret_uint32(sp, &ad->checksum), error); RCHECK(ret, krb5_ret_uint8(sp, &time_5ms), error); RCHECK(ret, krb5_ret_uint32(sp, &r_time_sec), error); if (strcmp(ad->pname, r_name) != 0 || strcmp(ad->pinst, r_instance) != 0 || strcmp(ad->prealm, r_realm) != 0) { ret = EINVAL; /* RD_AP_INCON */ goto error; } if (from_addr && from_addr != ad->address) { ret = EINVAL; /* RD_AP_BADD */ goto error; } gettimeofday(&tv, NULL); delta_t = abs((int)(tv.tv_sec - r_time_sec)); if (delta_t > CLOCK_SKEW) { ret = EINVAL; /* RD_AP_TIME */ goto error; } /* Now check for expiration of ticket */ tkt_age = tv.tv_sec - ad->time_sec; if ((tkt_age < 0) && (-tkt_age > CLOCK_SKEW)) { ret = EINVAL; /* RD_AP_NYV */ goto error; } if (tv.tv_sec > _krb5_krb_life_to_time(ad->time_sec, ad->life)) { ret = EINVAL; /* RD_AP_EXP */ goto error; } ret = 0; error: krb5_data_free(&ticket); krb5_data_free(&eaut); krb5_data_free(&aut); if (realm) free(realm); if (sname) free(sname); if (sinstance) free(sinstance); if (r_name) free(r_name); if (r_instance) free(r_instance); if (r_realm) free(r_realm); if (sp) krb5_storage_free(sp); if (ret) krb5_clear_error_string(context); return ret; }
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); }