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); }
/* * Previously this code returned either a v4 key or a v5 key and you * could tell from the enctype of the v5 key whether the v4 key was * useful. Now we return both keys so the code can try both des3 and * des decryption. We fail if the ticket doesn't have a v4 key. * Also, note as a side effect, the v5 key is basically useless in * the client case. It is still returned so the caller can free it. */ static int kerb_get_principal(char *name, char *inst, /* could have wild cards */ Principal *principal, int *more, /* more tuples than room for */ krb5_keyblock *k5key, krb5_kvno kvno, int issrv, /* true if retrieving a service key */ krb5_deltat *k5life) { /* Note that this structure should not be passed to the krb5_free* functions, because the pointers within it point to data with other references. */ krb5_principal search; krb5_db_entry entries; /* filled in by krb5_db_get_principal() */ int nprinc; /* how many found */ krb5_boolean more5; /* are there more? */ C_Block k; short toggle = 0; unsigned long *date; char* text; struct tm *tp; krb5_key_data *pkey; krb5_error_code retval; *more = 0; /* begin setting up the principal structure * with the first info we have: */ memcpy( principal->name, name, 1 + strlen( name)); memcpy( principal->instance, inst, 1 + strlen( inst)); /* the principal-name format changed between v4 & v5: * v4: name.instance@realm * v5: realm/name/instance * in v5, null instance means the null-component doesn't exist. */ if ((retval = krb5_425_conv_principal(kdc_context, name, inst, local_realm, &search))) return(0); if ((retval = krb5_db_get_principal(kdc_context, search, &entries, &nprinc, &more5))) { krb5_free_principal(kdc_context, search); return(0); } principal->key_low = principal->key_high = 0; krb5_free_principal(kdc_context, search); if (nprinc < 1) { *more = (int)more5 || (nprinc > 1); return(nprinc); } if (!issrv) { if (krb5_dbe_find_enctype(kdc_context, &entries, ENCTYPE_DES_CBC_CRC, KRB5_KDB_SALTTYPE_V4, kvno, &pkey) && krb5_dbe_find_enctype(kdc_context, &entries, ENCTYPE_DES_CBC_CRC, -1, kvno, &pkey)) { lt = klog(L_KRB_PERR, "KDC V4: principal %s.%s isn't V4 compatible", name, inst); krb5_db_free_principal(kdc_context, &entries, nprinc); return(0); } } else { if ( krb5_dbe_find_enctype(kdc_context, &entries, ENCTYPE_DES_CBC_CRC, KRB5_KDB_SALTTYPE_V4, kvno, &pkey) && krb5_dbe_find_enctype(kdc_context, &entries, ENCTYPE_DES_CBC_CRC, -1, kvno, &pkey)) { lt = klog(L_KRB_PERR, "KDC V4: failed to find key for %s.%s #%d", name, inst, kvno); krb5_db_free_principal(kdc_context, &entries, nprinc); return(0); } } if (!compat_decrypt_key(pkey, k, k5key, issrv)) { memcpy( &principal->key_low, k, LONGLEN); memcpy( &principal->key_high, (krb5_ui_4 *) k + 1, LONGLEN); } memset(k, 0, sizeof k); if (issrv) { krb5_free_keyblock_contents (kdc_context, k5key); if (krb5_dbe_find_enctype(kdc_context, &entries, ENCTYPE_DES3_CBC_RAW, -1, kvno, &pkey) && krb5_dbe_find_enctype(kdc_context, &entries, ENCTYPE_DES3_CBC_SHA1, -1, kvno, &pkey) && krb5_dbe_find_enctype(kdc_context, &entries, ENCTYPE_DES_CBC_CRC, KRB5_KDB_SALTTYPE_V4, kvno, &pkey) && krb5_dbe_find_enctype(kdc_context, &entries, ENCTYPE_DES_CBC_CRC, -1, kvno, &pkey)) { lt = klog(L_KRB_PERR, "KDC V4: failed to find key for %s.%s #%d (after having found it once)", name, inst, kvno); krb5_db_free_principal(kdc_context, &entries, nprinc); return(0); } compat_decrypt_key(pkey, k, k5key, issrv); memset (k, 0, sizeof k); } /* * Convert v5's entries struct to v4's Principal struct: * v5's time-unit for lifetimes is 1 sec, while v4 uses 5 minutes, * and gets weirder above (128 * 300) seconds. */ principal->max_life = krb_time_to_life(0, entries.max_life); if (k5life != NULL) *k5life = entries.max_life; /* * This is weird, but the intent is that the expiration is the minimum * of the principal expiration and key expiration */ principal->exp_date = (unsigned long) entries.expiration && entries.pw_expiration ? min(entries.expiration, entries.pw_expiration) : (entries.pw_expiration ? entries.pw_expiration : entries.expiration); /* principal->mod_date = (unsigned long) entries.mod_date; */ /* Set the master key version to 1. It's not really useful because all keys * will be encrypted in the same master key version, and digging out the * actual key version will be harder than it's worth --proven */ /* principal->kdc_key_ver = entries.mkvno; */ principal->kdc_key_ver = 1; principal->key_version = pkey->key_data_kvno; /* We overload the attributes with the relevant v5 ones */ principal->attributes = 0; if (isflagset(entries.attributes, KRB5_KDB_REQUIRES_HW_AUTH) || isflagset(entries.attributes, KRB5_KDB_REQUIRES_PRE_AUTH)) { principal->attributes |= V4_KDB_REQUIRES_PREAUTH; } if (isflagset(entries.attributes, KRB5_KDB_DISALLOW_ALL_TIX)) { principal->attributes |= V4_KDB_DISALLOW_ALL_TIX; } if (issrv && isflagset(entries.attributes, KRB5_KDB_DISALLOW_SVR)) { principal->attributes |= V4_KDB_DISALLOW_SVR; } if (isflagset(entries.attributes, KRB5_KDB_REQUIRES_PWCHANGE)) { principal->attributes |= V4_KDB_REQUIRES_PWCHANGE; } /* set up v4 format of each date's text: */ for ( date = &principal->exp_date, text = principal->exp_date_txt; toggle ^= 1; date = &principal->mod_date, text = principal->mod_date_txt) { tp = localtime( (time_t *) date); sprintf( text, "%4d-%02d-%02d", tp->tm_year > 1900 ? tp->tm_year : tp->tm_year + 1900, tp->tm_mon + 1, tp->tm_mday); /* January is 0, not 1 */ } /* * free the storage held by the v5 entry struct, * which was allocated by krb5_db_get_principal(). * this routine clears the keyblock's contents for us. */ krb5_db_free_principal(kdc_context, &entries, nprinc); *more = (int) more5 || (nprinc > 1); return( nprinc); }
void kerberos_v4(struct sockaddr_in *client, KTEXT pkt) { static KTEXT_ST rpkt_st; KTEXT rpkt = &rpkt_st; static KTEXT_ST ciph_st; KTEXT ciph = &ciph_st; static KTEXT_ST tk_st; KTEXT tk = &tk_st; static KTEXT_ST auth_st; KTEXT auth = &auth_st; AUTH_DAT ad_st; AUTH_DAT *ad = &ad_st; static struct in_addr client_host; static int msg_byte_order; static int swap_bytes; static u_char k_flags; /* char *p_name, *instance; */ int lifetime = 0; int i; C_Block key; Key_schedule key_s; char *ptr; krb5_keyblock k5key; krb5_kvno kvno; krb5_deltat sk5life, ck5life; KRB4_32 v4endtime, v4req_end; k5key.contents = NULL; /* in case we have to free it */ ciph->length = 0; client_host = client->sin_addr; /* eval macros and correct the byte order and alignment as needed */ req_version = pkt_version(pkt); /* 1 byte, version */ req_msg_type = pkt_msg_type(pkt); /* 1 byte, Kerberos msg type */ /* set these to point to something safe */ req_name_ptr = req_inst_ptr = req_realm_ptr = ""; /* check if disabled, but we tell client */ if (kdc_v4 == KDC_V4_DISABLE) { lt = klog(L_KRB_PERR, "KRB will not handle v4 request from %s", inet_ntoa(client_host)); /* send an error reply */ req_name_ptr = req_inst_ptr = req_realm_ptr = ""; kerb_err_reply(client, pkt, KERB_ERR_PKT_VER, lt); return; } /* check packet version */ if (req_version != KRB_PROT_VERSION) { lt = klog(L_KRB_PERR, "KRB prot version mismatch: KRB =%d request = %d", KRB_PROT_VERSION, req_version, 0); /* send an error reply */ req_name_ptr = req_inst_ptr = req_realm_ptr = ""; kerb_err_reply(client, pkt, KERB_ERR_PKT_VER, lt); return; } msg_byte_order = req_msg_type & 1; swap_bytes = 0; if (msg_byte_order != HOST_BYTE_ORDER) { swap_bytes++; } klog(L_KRB_PINFO, "Prot version: %d, Byte order: %d, Message type: %d", (int) req_version, msg_byte_order, req_msg_type); switch (req_msg_type & ~1) { case AUTH_MSG_KDC_REQUEST: { int req_life; /* Requested liftime */ unsigned int request_backdate = 0; /*How far to backdate in seconds.*/ char *service; /* Service name */ char *instance; /* Service instance */ #ifdef notdef int kerno; /* Kerberos error number */ #endif n_auth_req++; tk->length = 0; k_flags = 0; /* various kerberos flags */ /* set up and correct for byte order and alignment */ req_name_ptr = (char *) pkt_a_name(pkt); str_length_check(req_name_ptr, ANAME_SZ); req_inst_ptr = (char *) pkt_a_inst(pkt); str_length_check(req_inst_ptr, INST_SZ); req_realm_ptr = (char *) pkt_a_realm(pkt); str_length_check(req_realm_ptr, REALM_SZ); memcpy(&req_time_ws, pkt_time_ws(pkt), sizeof(req_time_ws)); /* time has to be diddled */ if (swap_bytes) { swap_u_long(req_time_ws); } ptr = (char *) pkt_time_ws(pkt) + 4; req_life = (*ptr++) & 0xff; service = ptr; str_length_check(service, SNAME_SZ); instance = ptr + strlen(service) + 1; str_length_check(instance, INST_SZ); rpkt = &rpkt_st; klog(L_INI_REQ, "Initial ticket request Host: %s User: \"%s\" \"%s\"", inet_ntoa(client_host), req_name_ptr, req_inst_ptr, 0); if ((i = check_princ(req_name_ptr, req_inst_ptr, 0, &a_name_data, &k5key, 0, &ck5life))) { kerb_err_reply(client, pkt, i, "check_princ failed"); a_name_data.key_low = a_name_data.key_high = 0; krb5_free_keyblock_contents(kdc_context, &k5key); return; } /* don't use k5key for client */ krb5_free_keyblock_contents(kdc_context, &k5key); tk->length = 0; /* init */ if (strcmp(service, "krbtgt")) klog(L_NTGT_INTK, "INITIAL request from %s.%s for %s.%s", req_name_ptr, req_inst_ptr, service, instance, 0); /* this does all the checking */ if ((i = check_princ(service, instance, lifetime, &s_name_data, &k5key, 1, &sk5life))) { kerb_err_reply(client, pkt, i, "check_princ failed"); a_name_data.key_high = a_name_data.key_low = 0; s_name_data.key_high = s_name_data.key_low = 0; krb5_free_keyblock_contents(kdc_context, &k5key); return; } /* Bound requested lifetime with service and user */ v4req_end = krb_life_to_time(kerb_time.tv_sec, req_life); v4req_end = min(v4req_end, kerb_time.tv_sec + ck5life); v4req_end = min(v4req_end, kerb_time.tv_sec + sk5life); lifetime = krb_time_to_life(kerb_time.tv_sec, v4req_end); v4endtime = krb_life_to_time(kerb_time.tv_sec, lifetime); /* * Adjust issue time backwards if necessary, due to * roundup in krb_time_to_life(). */ if (v4endtime > v4req_end) request_backdate = v4endtime - v4req_end; #ifdef NOENCRYPTION memset(session_key, 0, sizeof(C_Block)); #else /* random session key */ des_new_random_key(session_key); #endif /* unseal server's key from master key */ memcpy( key, &s_name_data.key_low, 4); memcpy( ((krb5_ui_4 *) key) + 1, &s_name_data.key_high, 4); s_name_data.key_low = s_name_data.key_high = 0; kdb_encrypt_key(key, key, master_key, master_key_schedule, DECRYPT); /* construct and seal the ticket */ /* We always issue des tickets; the 3des tickets are a broken hack*/ krb_create_ticket(tk, k_flags, a_name_data.name, a_name_data.instance, local_realm, client_host.s_addr, (char *) session_key, lifetime, kerb_time.tv_sec - request_backdate, s_name_data.name, s_name_data.instance, key); krb5_free_keyblock_contents(kdc_context, &k5key); memset(key, 0, sizeof(key)); memset(key_s, 0, sizeof(key_s)); /* * get the user's key, unseal it from the server's key, and * use it to seal the cipher */ /* a_name_data.key_low a_name_data.key_high */ memcpy( key, &a_name_data.key_low, 4); memcpy( ((krb5_ui_4 *) key) + 1, &a_name_data.key_high, 4); a_name_data.key_low= a_name_data.key_high = 0; /* unseal the a_name key from the master key */ kdb_encrypt_key(key, key, master_key, master_key_schedule, DECRYPT); create_ciph(ciph, session_key, s_name_data.name, s_name_data.instance, local_realm, lifetime, s_name_data.key_version, tk, kerb_time.tv_sec, key); /* clear session key */ memset(session_key, 0, sizeof(session_key)); memset(key, 0, sizeof(key)); /* always send a reply packet */ rpkt = create_auth_reply(req_name_ptr, req_inst_ptr, req_realm_ptr, req_time_ws, 0, a_name_data.exp_date, a_name_data.key_version, ciph); krb4_sendto(f, (char *) rpkt->dat, rpkt->length, 0, (struct sockaddr *) client, sizeof (struct sockaddr_in)); memset(&a_name_data, 0, sizeof(a_name_data)); memset(&s_name_data, 0, sizeof(s_name_data)); break; } case AUTH_MSG_APPL_REQUEST: { krb5_ui_4 time_ws; /* Workstation time */ int req_life; /* Requested liftime */ char *service; /* Service name */ char *instance; /* Service instance */ int kerno = 0; /* Kerberos error number */ unsigned int request_backdate = 0; /*How far to backdate in seconds.*/ char tktrlm[REALM_SZ]; n_appl_req++; tk->length = 0; k_flags = 0; /* various kerberos flags */ auth->mbz = 0; /* pkt->mbz already zeroed */ auth->length = 4 + strlen((char *)pkt->dat + 3); if (auth->length + 1 > MAX_KTXT_LEN) { lt = klog(L_KRB_PERR, "APPL request with realm length too long from %s", inet_ntoa(client_host)); kerb_err_reply(client, pkt, RD_AP_INCON, "realm length too long"); return; } auth->length += (int) *(pkt->dat + auth->length) + (int) *(pkt->dat + auth->length + 1) + 2; if (auth->length > MAX_KTXT_LEN) { lt = klog(L_KRB_PERR, "APPL request with funky tkt or req_id length from %s", inet_ntoa(client_host)); kerb_err_reply(client, pkt, RD_AP_INCON, "funky tkt or req_id length"); return; } memcpy(auth->dat, pkt->dat, auth->length); strncpy(tktrlm, (char *)auth->dat + 3, REALM_SZ); tktrlm[REALM_SZ-1] = '\0'; kvno = (krb5_kvno)auth->dat[2]; if ((!allow_v4_crossrealm)&&strcmp(tktrlm, local_realm) != 0) { lt = klog(L_ERR_UNK, "Cross realm ticket from %s denied by policy,", tktrlm); kerb_err_reply(client, pkt, KERB_ERR_PRINCIPAL_UNKNOWN, lt); return; } if (set_tgtkey(tktrlm, kvno, 0)) { lt = klog(L_ERR_UNK, "FAILED set_tgtkey realm %s, kvno %d. Host: %s ", tktrlm, kvno, inet_ntoa(client_host)); /* no better error code */ kerb_err_reply(client, pkt, KERB_ERR_PRINCIPAL_UNKNOWN, lt); return; } kerno = krb_rd_req(auth, "krbtgt", tktrlm, client_host.s_addr, ad, 0); if (kerno) { if (set_tgtkey(tktrlm, kvno, 1)) { lt = klog(L_ERR_UNK, "FAILED 3des set_tgtkey realm %s, kvno %d. Host: %s ", tktrlm, kvno, inet_ntoa(client_host)); /* no better error code */ kerb_err_reply(client, pkt, KERB_ERR_PRINCIPAL_UNKNOWN, lt); return; } kerno = krb_rd_req(auth, "krbtgt", tktrlm, client_host.s_addr, ad, 0); } if (kerno) { klog(L_ERR_UNK, "FAILED krb_rd_req from %s: %s", inet_ntoa(client_host), krb_get_err_text(kerno)); req_name_ptr = req_inst_ptr = req_realm_ptr = ""; kerb_err_reply(client, pkt, kerno, "krb_rd_req failed"); return; } ptr = (char *) pkt->dat + auth->length; memcpy(&time_ws, ptr, 4); ptr += 4; req_life = (*ptr++) & 0xff; service = ptr; str_length_check(service, SNAME_SZ); instance = ptr + strlen(service) + 1; str_length_check(instance, INST_SZ); klog(L_APPL_REQ, "APPL Request %s.%s@%s on %s for %s.%s", ad->pname, ad->pinst, ad->prealm, inet_ntoa(client_host), service, instance, 0); req_name_ptr = ad->pname; req_inst_ptr = ad->pinst; req_realm_ptr = ad->prealm; if (strcmp(ad->prealm, tktrlm)) { kerb_err_reply(client, pkt, KERB_ERR_PRINCIPAL_UNKNOWN, "Can't hop realms"); return; } if (!strcmp(service, "changepw")) { kerb_err_reply(client, pkt, KERB_ERR_PRINCIPAL_UNKNOWN, "Can't authorize password changed based on TGT"); return; } kerno = check_princ(service, instance, req_life, &s_name_data, &k5key, 1, &sk5life); if (kerno) { kerb_err_reply(client, pkt, kerno, "check_princ failed"); s_name_data.key_high = s_name_data.key_low = 0; krb5_free_keyblock_contents(kdc_context, &k5key); return; } /* Bound requested lifetime with service and user */ v4endtime = krb_life_to_time((KRB4_32)ad->time_sec, ad->life); v4req_end = krb_life_to_time(kerb_time.tv_sec, req_life); v4req_end = min(v4endtime, v4req_end); v4req_end = min(v4req_end, kerb_time.tv_sec + sk5life); lifetime = krb_time_to_life(kerb_time.tv_sec, v4req_end); v4endtime = krb_life_to_time(kerb_time.tv_sec, lifetime); /* * Adjust issue time backwards if necessary, due to * roundup in krb_time_to_life(). */ if (v4endtime > v4req_end) request_backdate = v4endtime - v4req_end; /* unseal server's key from master key */ memcpy(key, &s_name_data.key_low, 4); memcpy(((krb5_ui_4 *) key) + 1, &s_name_data.key_high, 4); s_name_data.key_low = s_name_data.key_high = 0; kdb_encrypt_key(key, key, master_key, master_key_schedule, DECRYPT); /* construct and seal the ticket */ #ifdef NOENCRYPTION memset(session_key, 0, sizeof(C_Block)); #else /* random session key */ des_new_random_key(session_key); #endif /* ALways issue des tickets*/ krb_create_ticket(tk, k_flags, ad->pname, ad->pinst, ad->prealm, client_host.s_addr, (char *) session_key, lifetime, kerb_time.tv_sec - request_backdate, s_name_data.name, s_name_data.instance, key); krb5_free_keyblock_contents(kdc_context, &k5key); memset(key, 0, sizeof(key)); memset(key_s, 0, sizeof(key_s)); create_ciph(ciph, session_key, service, instance, local_realm, lifetime, s_name_data.key_version, tk, kerb_time.tv_sec, ad->session); /* clear session key */ memset(session_key, 0, sizeof(session_key)); memset(ad->session, 0, sizeof(ad->session)); rpkt = create_auth_reply(ad->pname, ad->pinst, ad->prealm, time_ws, 0, 0, 0, ciph); krb4_sendto(f, (char *) rpkt->dat, rpkt->length, 0, (struct sockaddr *) client, sizeof (struct sockaddr_in)); memset(&s_name_data, 0, sizeof(s_name_data)); break; } #ifdef notdef_DIE case AUTH_MSG_DIE: { lt = klog(L_DEATH_REQ, "Host: %s User: \"%s\" \"%s\" Kerberos killed", inet_ntoa(client_host), req_name_ptr, req_inst_ptr, 0); exit(0); } #endif /* notdef_DIE */ default: { lt = klog(L_KRB_PERR, "Unknown message type: %d from %s port %u", req_msg_type, inet_ntoa(client_host), ntohs(client->sin_port)); break; } } }
static int _pam_krb5_v4_init(krb5_context ctx, struct _pam_krb5_stash *stash, struct _pam_krb5_user_info *user, struct _pam_krb5_options *options, char *sname, char *sinstance, char *password, int *result) { char name[ANAME_SZ + 1], instance[INST_SZ + 1], realm[REALM_SZ + 1]; char pname[ANAME_SZ + 1], pinstance[INST_SZ + 1]; char tktfile[PATH_MAX]; char *saved_tktstring; int life, i, fd; struct stat st; /* Convert the krb5 version of the principal's name to a v4 principal * name. This may involve changing "host" to "rcmd" and so on, so let * libkrb5 handle it. */ memset(name, '\0', sizeof(name)); memset(instance, '\0', sizeof(instance)); memset(realm, '\0', sizeof(realm)); i = krb5_524_conv_principal(ctx, user->principal_name, name, instance, realm); if (i != 0) { if (result) { *result = i; } return PAM_SERVICE_ERR; } if (options->debug) { debug("converted principal to '%s%s%s%s@'%s'", name, strlen(instance) ? "'.'" : "'", instance, strlen(instance) ? "'" : "", realm); } #ifdef HAVE_KRB_TIME_TO_LIFE /* Convert the ticket lifetime of the v5 credentials into a v4 * lifetime, which is the X coordinate along a curve where Y is the * actual length. Again, this is magic. */ life = krb_time_to_life(stash->v5creds.times.starttime, stash->v5creds.times.endtime); #else /* No life_to_time() function means that we have to estimate the * intended lifetime, in 5-minute increments. We also have a maximum * value to contend with, because the lifetime is expressed in a single * byte. */ life = stash->v5creds.times.endtime - stash->v5creds.times.starttime; life /= (60 * 5); if (life > 0xff) { life = 0xff; } #endif /* Create the ticket file. One of two things will happen here. Either * libkrb[4] will just use the file, and we're safer because it * wouldn't have used O_EXCL to do so, or it will nuke the file and * reopen it with O_EXCL. In the latter case, the descriptor we have * will become useless, so we don't actually use it for anything. */ #ifdef HAVE_LONG_LONG snprintf(tktfile, sizeof(tktfile), "%s/tkt%llu_XXXXXX", options->ccache_dir, options->user_check ? (unsigned long long) user->uid : (unsigned long long) getuid()); #else snprintf(tktfile, sizeof(tktfile), "%s/tkt%lu_XXXXXX", options->ccache_dir, options->user_check ? (unsigned long) user->uid : (unsigned long) getuid()); #endif fd = mkstemp(tktfile); if (fd == -1) { if (result) { *result = errno; } return PAM_SERVICE_ERR; } if (fchown(fd, getuid(), getgid()) != 0) { warn("error setting permissions on \"%s\" (%s), attempting " "to continue", tktfile, strerror(errno)); } if (options->debug) { debug("preparing to place v4 credentials in '%s'", tktfile); } /* Save the old default ticket file name, and set the default to use * our just-created empty file. */ saved_tktstring = xstrdup(tkt_string()); krb_set_tkt_string(tktfile); /* Get the initial credentials. */ i = krb_get_pw_in_tkt(name, instance, realm, sname, sinstance ? sinstance : realm, life, password); if (result) { *result = i; } /* Restore the original default ticket file name. */ krb_set_tkt_string(saved_tktstring); xstrfree(saved_tktstring); saved_tktstring = NULL; /* If we got credentials, read them from the file, and then remove the * file. */ if (i == 0) { i = tf_init(tktfile, R_TKT_FIL); if (i == 0) { i = tf_get_pname(pname); if (i == 0) { i = tf_get_pinst(pinstance); if (i == 0) { i = tf_get_cred(&stash->v4creds); if (i == 0) { tf_close(); unlink(tktfile); close(fd); return PAM_SUCCESS; } else { warn("error reading creds " "from '%s': %d (%s)", tktfile, i, v5_error_message(i)); } } else { warn("error reading instance from '%s'" ": %d (%s)", tktfile, i, v5_error_message(i)); } } else { warn("error reading principal name from '%s'" ": %d (%s)", tktfile, i, v5_error_message(i)); } tf_close(); } else { const char *tferror; switch (i) { case NO_TKT_FIL: tferror = "no ticket file"; break; case TKT_FIL_ACC: tferror = "ticket file had wrong permissions"; break; case TKT_FIL_LCK: tferror = "error locking ticket file"; break; default: tferror = strerror(errno); break; } warn("error opening '%s' for reading: %s", tktfile, tferror); if ((i == TKT_FIL_ACC) && (options->debug)) { if (stat(tktfile, &st) == 0) { debug("file owner is %lu:%lu, " "we are effective %lu:%lu, " "real %lu:%lu", (unsigned long) st.st_uid, (unsigned long) st.st_gid, (unsigned long) geteuid(), (unsigned long) getegid(), (unsigned long) getuid(), (unsigned long) getgid()); } } } } unlink(tktfile); close(fd); return PAM_AUTH_ERR; }