10Sstevel@tonic-gate /*
20Sstevel@tonic-gate * kdc/kdc_util.c
30Sstevel@tonic-gate *
40Sstevel@tonic-gate * Copyright 1990,1991 by the Massachusetts Institute of Technology.
50Sstevel@tonic-gate * All Rights Reserved.
60Sstevel@tonic-gate *
70Sstevel@tonic-gate * Export of this software from the United States of America may
80Sstevel@tonic-gate * require a specific license from the United States Government.
90Sstevel@tonic-gate * It is the responsibility of any person or organization contemplating
100Sstevel@tonic-gate * export to obtain such a license before exporting.
110Sstevel@tonic-gate *
120Sstevel@tonic-gate * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
130Sstevel@tonic-gate * distribute this software and its documentation for any purpose and
140Sstevel@tonic-gate * without fee is hereby granted, provided that the above copyright
150Sstevel@tonic-gate * notice appear in all copies and that both that copyright notice and
160Sstevel@tonic-gate * this permission notice appear in supporting documentation, and that
170Sstevel@tonic-gate * the name of M.I.T. not be used in advertising or publicity pertaining
180Sstevel@tonic-gate * to distribution of the software without specific, written prior
190Sstevel@tonic-gate * permission. Furthermore if you modify this software you must label
200Sstevel@tonic-gate * your software as modified software and not distribute it in such a
210Sstevel@tonic-gate * fashion that it might be confused with the original M.I.T. software.
220Sstevel@tonic-gate * M.I.T. makes no representations about the suitability of
230Sstevel@tonic-gate * this software for any purpose. It is provided "as is" without express
240Sstevel@tonic-gate * or implied warranty.
250Sstevel@tonic-gate *
260Sstevel@tonic-gate *
270Sstevel@tonic-gate * Utility functions for the KDC implementation.
280Sstevel@tonic-gate */
290Sstevel@tonic-gate
300Sstevel@tonic-gate
310Sstevel@tonic-gate
320Sstevel@tonic-gate #include "k5-int.h"
330Sstevel@tonic-gate #include "kdc_util.h"
340Sstevel@tonic-gate #include "extern.h"
350Sstevel@tonic-gate #include <stdio.h>
362881Smp153739 #include <ctype.h>
370Sstevel@tonic-gate #include <syslog.h>
380Sstevel@tonic-gate #include "adm.h"
390Sstevel@tonic-gate #include "adm_proto.h"
402881Smp153739 #include <limits.h>
410Sstevel@tonic-gate
420Sstevel@tonic-gate #ifdef USE_RCACHE
430Sstevel@tonic-gate static char *kdc_current_rcname = (char *) NULL;
440Sstevel@tonic-gate krb5_deltat rc_lifetime; /* See kdc_initialize_rcache() */
450Sstevel@tonic-gate #endif
460Sstevel@tonic-gate
470Sstevel@tonic-gate #ifdef USE_RCACHE
480Sstevel@tonic-gate /*
490Sstevel@tonic-gate * initialize the replay cache.
500Sstevel@tonic-gate */
510Sstevel@tonic-gate krb5_error_code
kdc_initialize_rcache(krb5_context kcontext,char * rcache_name)522881Smp153739 kdc_initialize_rcache(krb5_context kcontext, char *rcache_name)
530Sstevel@tonic-gate {
540Sstevel@tonic-gate krb5_error_code retval;
550Sstevel@tonic-gate char *rcname;
560Sstevel@tonic-gate char *sname;
570Sstevel@tonic-gate
580Sstevel@tonic-gate rcname = (rcache_name) ? rcache_name : kdc_current_rcname;
590Sstevel@tonic-gate
600Sstevel@tonic-gate /* rc_lifetime used elsewhere to verify we're not */
610Sstevel@tonic-gate /* replaying really old data */
620Sstevel@tonic-gate rc_lifetime = kcontext->clockskew;
630Sstevel@tonic-gate
640Sstevel@tonic-gate if (!rcname)
650Sstevel@tonic-gate rcname = KDCRCACHE;
660Sstevel@tonic-gate if (!(retval = krb5_rc_resolve_full(kcontext, &kdc_rcache, rcname))) {
670Sstevel@tonic-gate /* Recover or initialize the replay cache */
680Sstevel@tonic-gate if (!(retval = krb5_rc_recover(kcontext, kdc_rcache)) ||
690Sstevel@tonic-gate !(retval = krb5_rc_initialize(kcontext,
700Sstevel@tonic-gate kdc_rcache,
710Sstevel@tonic-gate kcontext->clockskew))
720Sstevel@tonic-gate ) {
730Sstevel@tonic-gate /* Expunge the replay cache */
740Sstevel@tonic-gate if (!(retval = krb5_rc_expunge(kcontext, kdc_rcache))) {
750Sstevel@tonic-gate sname = kdc_current_rcname;
760Sstevel@tonic-gate kdc_current_rcname = strdup(rcname);
770Sstevel@tonic-gate if (sname)
780Sstevel@tonic-gate free(sname);
790Sstevel@tonic-gate }
800Sstevel@tonic-gate }
810Sstevel@tonic-gate if (retval)
820Sstevel@tonic-gate krb5_rc_close(kcontext, kdc_rcache);
830Sstevel@tonic-gate }
840Sstevel@tonic-gate return(retval);
850Sstevel@tonic-gate }
860Sstevel@tonic-gate #endif
870Sstevel@tonic-gate
880Sstevel@tonic-gate /*
890Sstevel@tonic-gate * concatenate first two authdata arrays, returning an allocated replacement.
900Sstevel@tonic-gate * The replacement should be freed with krb5_free_authdata().
910Sstevel@tonic-gate */
920Sstevel@tonic-gate krb5_error_code
concat_authorization_data(krb5_authdata ** first,krb5_authdata ** second,krb5_authdata *** output)932881Smp153739 concat_authorization_data(krb5_authdata **first, krb5_authdata **second,
942881Smp153739 krb5_authdata ***output)
950Sstevel@tonic-gate {
960Sstevel@tonic-gate register int i, j;
970Sstevel@tonic-gate register krb5_authdata **ptr, **retdata;
980Sstevel@tonic-gate
990Sstevel@tonic-gate /* count up the entries */
1000Sstevel@tonic-gate i = 0;
1010Sstevel@tonic-gate if (first)
1020Sstevel@tonic-gate for (ptr = first; *ptr; ptr++)
1030Sstevel@tonic-gate i++;
1040Sstevel@tonic-gate if (second)
1050Sstevel@tonic-gate for (ptr = second; *ptr; ptr++)
1060Sstevel@tonic-gate i++;
1070Sstevel@tonic-gate
1080Sstevel@tonic-gate retdata = (krb5_authdata **)malloc((i+1)*sizeof(*retdata));
1090Sstevel@tonic-gate if (!retdata)
1100Sstevel@tonic-gate return ENOMEM;
1110Sstevel@tonic-gate retdata[i] = 0; /* null-terminated array */
1120Sstevel@tonic-gate for (i = 0, j = 0, ptr = first; j < 2 ; ptr = second, j++)
1130Sstevel@tonic-gate while (ptr && *ptr) {
1140Sstevel@tonic-gate /* now walk & copy */
1150Sstevel@tonic-gate retdata[i] = (krb5_authdata *)malloc(sizeof(*retdata[i]));
1160Sstevel@tonic-gate if (!retdata[i]) {
1170Sstevel@tonic-gate krb5_free_authdata(kdc_context, retdata);
1180Sstevel@tonic-gate return ENOMEM;
1190Sstevel@tonic-gate }
1200Sstevel@tonic-gate *retdata[i] = **ptr;
1210Sstevel@tonic-gate if (!(retdata[i]->contents =
1220Sstevel@tonic-gate (krb5_octet *)malloc(retdata[i]->length))) {
1230Sstevel@tonic-gate free((char *)retdata[i]);
1240Sstevel@tonic-gate retdata[i] = 0;
1250Sstevel@tonic-gate krb5_free_authdata(kdc_context, retdata);
1260Sstevel@tonic-gate return ENOMEM;
1270Sstevel@tonic-gate }
1280Sstevel@tonic-gate memcpy((char *) retdata[i]->contents,
1290Sstevel@tonic-gate (char *)(*ptr)->contents,
1300Sstevel@tonic-gate retdata[i]->length);
1310Sstevel@tonic-gate
1320Sstevel@tonic-gate ptr++;
1330Sstevel@tonic-gate i++;
1340Sstevel@tonic-gate }
1350Sstevel@tonic-gate *output = retdata;
1360Sstevel@tonic-gate return 0;
1370Sstevel@tonic-gate }
1380Sstevel@tonic-gate
1390Sstevel@tonic-gate krb5_boolean
realm_compare(krb5_principal princ1,krb5_principal princ2)1402881Smp153739 realm_compare(krb5_principal princ1, krb5_principal princ2)
1410Sstevel@tonic-gate {
1420Sstevel@tonic-gate krb5_data *realm1 = krb5_princ_realm(kdc_context, princ1);
1430Sstevel@tonic-gate krb5_data *realm2 = krb5_princ_realm(kdc_context, princ2);
1440Sstevel@tonic-gate
1450Sstevel@tonic-gate return((realm1->length == realm2->length) &&
1460Sstevel@tonic-gate !memcmp(realm1->data, realm2->data, realm1->length));
1470Sstevel@tonic-gate }
1480Sstevel@tonic-gate
1490Sstevel@tonic-gate /*
1500Sstevel@tonic-gate * Returns TRUE if the kerberos principal is the name of a Kerberos ticket
1510Sstevel@tonic-gate * service.
1520Sstevel@tonic-gate */
krb5_is_tgs_principal(krb5_principal principal)1532881Smp153739 krb5_boolean krb5_is_tgs_principal(krb5_principal principal)
1540Sstevel@tonic-gate {
1552881Smp153739 if ((krb5_princ_size(kdc_context, principal) > 0) &&
1560Sstevel@tonic-gate (krb5_princ_component(kdc_context, principal, 0)->length ==
1570Sstevel@tonic-gate KRB5_TGS_NAME_SIZE) &&
1580Sstevel@tonic-gate (!memcmp(krb5_princ_component(kdc_context, principal, 0)->data,
1590Sstevel@tonic-gate KRB5_TGS_NAME, KRB5_TGS_NAME_SIZE)))
1600Sstevel@tonic-gate return TRUE;
1610Sstevel@tonic-gate return FALSE;
1620Sstevel@tonic-gate }
1630Sstevel@tonic-gate
1640Sstevel@tonic-gate /*
1650Sstevel@tonic-gate * given authentication data (provides seed for checksum), verify checksum
1660Sstevel@tonic-gate * for source data.
1670Sstevel@tonic-gate */
1680Sstevel@tonic-gate static krb5_error_code
comp_cksum(krb5_context kcontext,krb5_data * source,krb5_ticket * ticket,krb5_checksum * his_cksum)1692881Smp153739 comp_cksum(krb5_context kcontext, krb5_data *source, krb5_ticket *ticket,
1702881Smp153739 krb5_checksum *his_cksum)
1710Sstevel@tonic-gate {
1720Sstevel@tonic-gate krb5_error_code retval;
1730Sstevel@tonic-gate krb5_boolean valid;
1740Sstevel@tonic-gate
175781Sgtb if (!krb5_c_valid_cksumtype(his_cksum->checksum_type))
1760Sstevel@tonic-gate return KRB5KDC_ERR_SUMTYPE_NOSUPP;
1770Sstevel@tonic-gate
1780Sstevel@tonic-gate /* must be collision proof */
179781Sgtb if (!krb5_c_is_coll_proof_cksum(his_cksum->checksum_type))
1800Sstevel@tonic-gate return KRB5KRB_AP_ERR_INAPP_CKSUM;
1810Sstevel@tonic-gate
1820Sstevel@tonic-gate /* verify checksum */
1830Sstevel@tonic-gate if ((retval = krb5_c_verify_checksum(kcontext, ticket->enc_part2->session,
1840Sstevel@tonic-gate KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM,
1850Sstevel@tonic-gate source, his_cksum, &valid)))
1860Sstevel@tonic-gate return(retval);
1870Sstevel@tonic-gate
1880Sstevel@tonic-gate if (!valid)
1890Sstevel@tonic-gate return(KRB5KRB_AP_ERR_BAD_INTEGRITY);
1900Sstevel@tonic-gate
1910Sstevel@tonic-gate return(0);
1920Sstevel@tonic-gate }
1930Sstevel@tonic-gate
1940Sstevel@tonic-gate krb5_error_code
kdc_process_tgs_req(krb5_kdc_req * request,const krb5_fulladdr * from,krb5_data * pkt,krb5_ticket ** ticket,krb5_keyblock ** subkey)1952881Smp153739 kdc_process_tgs_req(krb5_kdc_req *request, const krb5_fulladdr *from,
1962881Smp153739 krb5_data *pkt, krb5_ticket **ticket,
1972881Smp153739 krb5_keyblock **subkey)
1980Sstevel@tonic-gate {
1990Sstevel@tonic-gate krb5_pa_data ** tmppa;
2000Sstevel@tonic-gate krb5_ap_req * apreq;
2010Sstevel@tonic-gate krb5_error_code retval;
2020Sstevel@tonic-gate krb5_data scratch1;
2030Sstevel@tonic-gate krb5_data * scratch = NULL;
2040Sstevel@tonic-gate krb5_boolean foreign_server = FALSE;
2050Sstevel@tonic-gate krb5_auth_context auth_context = NULL;
2060Sstevel@tonic-gate krb5_authenticator * authenticator = NULL;
2070Sstevel@tonic-gate krb5_checksum * his_cksum = NULL;
2082881Smp153739 /* krb5_keyblock * key = NULL;*/
2092881Smp153739 /* krb5_kvno kvno = 0;*/
2100Sstevel@tonic-gate
2110Sstevel@tonic-gate if (!request->padata)
2120Sstevel@tonic-gate return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
2130Sstevel@tonic-gate for (tmppa = request->padata; *tmppa; tmppa++) {
2140Sstevel@tonic-gate if ((*tmppa)->pa_type == KRB5_PADATA_AP_REQ)
2150Sstevel@tonic-gate break;
2160Sstevel@tonic-gate }
2170Sstevel@tonic-gate if (!*tmppa) /* cannot find any AP_REQ */
2180Sstevel@tonic-gate return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
2190Sstevel@tonic-gate
2200Sstevel@tonic-gate scratch1.length = (*tmppa)->length;
2210Sstevel@tonic-gate scratch1.data = (char *)(*tmppa)->contents;
2220Sstevel@tonic-gate if ((retval = decode_krb5_ap_req(&scratch1, &apreq)))
2230Sstevel@tonic-gate return retval;
2240Sstevel@tonic-gate
2250Sstevel@tonic-gate if (isflagset(apreq->ap_options, AP_OPTS_USE_SESSION_KEY) ||
2260Sstevel@tonic-gate isflagset(apreq->ap_options, AP_OPTS_MUTUAL_REQUIRED)) {
2270Sstevel@tonic-gate krb5_klog_syslog(LOG_INFO, "TGS_REQ: SESSION KEY or MUTUAL");
2280Sstevel@tonic-gate retval = KRB5KDC_ERR_POLICY;
2290Sstevel@tonic-gate goto cleanup;
2300Sstevel@tonic-gate }
2310Sstevel@tonic-gate
2320Sstevel@tonic-gate /* If the "server" principal in the ticket is not something
2330Sstevel@tonic-gate in the local realm, then we must refuse to service the request
2340Sstevel@tonic-gate if the client claims to be from the local realm.
2350Sstevel@tonic-gate
2360Sstevel@tonic-gate If we don't do this, then some other realm's nasty KDC can
2370Sstevel@tonic-gate claim to be authenticating a client from our realm, and we'll
2380Sstevel@tonic-gate give out tickets concurring with it!
2390Sstevel@tonic-gate
2400Sstevel@tonic-gate we set a flag here for checking below.
2410Sstevel@tonic-gate */
2420Sstevel@tonic-gate if ((krb5_princ_realm(kdc_context, apreq->ticket->server)->length !=
2430Sstevel@tonic-gate krb5_princ_realm(kdc_context, tgs_server)->length) ||
2440Sstevel@tonic-gate memcmp(krb5_princ_realm(kdc_context, apreq->ticket->server)->data,
2450Sstevel@tonic-gate krb5_princ_realm(kdc_context, tgs_server)->data,
2460Sstevel@tonic-gate krb5_princ_realm(kdc_context, tgs_server)->length))
2470Sstevel@tonic-gate foreign_server = TRUE;
2480Sstevel@tonic-gate
2490Sstevel@tonic-gate if ((retval = krb5_auth_con_init(kdc_context, &auth_context)))
2500Sstevel@tonic-gate goto cleanup;
2510Sstevel@tonic-gate
2520Sstevel@tonic-gate if ((retval = krb5_auth_con_setaddrs(kdc_context, auth_context, NULL,
2530Sstevel@tonic-gate from->address)) )
2540Sstevel@tonic-gate goto cleanup_auth_context;
2550Sstevel@tonic-gate #ifdef USE_RCACHE
2560Sstevel@tonic-gate if ((retval = krb5_auth_con_setrcache(kdc_context, auth_context,
2570Sstevel@tonic-gate kdc_rcache)))
2580Sstevel@tonic-gate goto cleanup_auth_context;
2590Sstevel@tonic-gate #endif
2600Sstevel@tonic-gate
2610Sstevel@tonic-gate /*
2620Sstevel@tonic-gate if ((retval = kdc_get_server_key(apreq->ticket, &key, &kvno)))
2630Sstevel@tonic-gate goto cleanup_auth_context;
2640Sstevel@tonic-gate */
2650Sstevel@tonic-gate
2660Sstevel@tonic-gate /*
2670Sstevel@tonic-gate * XXX This is currently wrong but to fix it will require making a
2680Sstevel@tonic-gate * new keytab for groveling over the kdb.
2690Sstevel@tonic-gate */
2700Sstevel@tonic-gate /*
2710Sstevel@tonic-gate retval = krb5_auth_con_setuseruserkey(kdc_context, auth_context, key);
2720Sstevel@tonic-gate krb5_free_keyblock(kdc_context, key);
2730Sstevel@tonic-gate if (retval)
2740Sstevel@tonic-gate goto cleanup_auth_context;
2750Sstevel@tonic-gate */
2760Sstevel@tonic-gate
2770Sstevel@tonic-gate if ((retval = krb5_rd_req_decoded_anyflag(kdc_context, &auth_context, apreq,
2780Sstevel@tonic-gate apreq->ticket->server,
2790Sstevel@tonic-gate kdc_active_realm->realm_keytab,
2800Sstevel@tonic-gate NULL, ticket))) {
2810Sstevel@tonic-gate #ifdef USE_RCACHE
2820Sstevel@tonic-gate /*
2830Sstevel@tonic-gate * I'm not so sure that this is right, but it's better than nothing
2840Sstevel@tonic-gate * at all.
2850Sstevel@tonic-gate *
2860Sstevel@tonic-gate * If we choke in the rd_req because of the replay cache, then attempt
2870Sstevel@tonic-gate * to reinitialize the replay cache because somebody could have deleted
2880Sstevel@tonic-gate * it from underneath us (e.g. a cron job)
2890Sstevel@tonic-gate */
2900Sstevel@tonic-gate if ((retval == KRB5_RC_IO_IO) ||
2910Sstevel@tonic-gate (retval == KRB5_RC_IO_UNKNOWN)) {
2920Sstevel@tonic-gate (void) krb5_rc_close(kdc_context, kdc_rcache);
2930Sstevel@tonic-gate kdc_rcache = (krb5_rcache) NULL;
2940Sstevel@tonic-gate if (!(retval = kdc_initialize_rcache(kdc_context, (char *) NULL))) {
2950Sstevel@tonic-gate if ((retval = krb5_auth_con_setrcache(kdc_context, auth_context,
2960Sstevel@tonic-gate kdc_rcache)) ||
2970Sstevel@tonic-gate (retval = krb5_rd_req_decoded_anyflag(kdc_context, &auth_context,
2980Sstevel@tonic-gate apreq, apreq->ticket->server,
2990Sstevel@tonic-gate kdc_active_realm->realm_keytab,
3000Sstevel@tonic-gate NULL, ticket))
3010Sstevel@tonic-gate )
3020Sstevel@tonic-gate goto cleanup_auth_context;
3030Sstevel@tonic-gate }
3040Sstevel@tonic-gate } else
3050Sstevel@tonic-gate goto cleanup_auth_context;
3060Sstevel@tonic-gate #else
3070Sstevel@tonic-gate goto cleanup_auth_context;
3080Sstevel@tonic-gate #endif
3090Sstevel@tonic-gate }
3100Sstevel@tonic-gate
3110Sstevel@tonic-gate /* "invalid flag" tickets can must be used to validate */
3120Sstevel@tonic-gate if (isflagset((*ticket)->enc_part2->flags, TKT_FLG_INVALID)
3130Sstevel@tonic-gate && !isflagset(request->kdc_options, KDC_OPT_VALIDATE)) {
3140Sstevel@tonic-gate retval = KRB5KRB_AP_ERR_TKT_INVALID;
3150Sstevel@tonic-gate goto cleanup_auth_context;
3160Sstevel@tonic-gate }
3170Sstevel@tonic-gate
3182881Smp153739 if ((retval = krb5_auth_con_getrecvsubkey(kdc_context,
3192881Smp153739 auth_context, subkey)))
3200Sstevel@tonic-gate goto cleanup_auth_context;
3210Sstevel@tonic-gate
3220Sstevel@tonic-gate if ((retval = krb5_auth_con_getauthenticator(kdc_context, auth_context,
3230Sstevel@tonic-gate &authenticator)))
3240Sstevel@tonic-gate goto cleanup_auth_context;
3250Sstevel@tonic-gate
3260Sstevel@tonic-gate /* Check for a checksum */
3270Sstevel@tonic-gate if (!(his_cksum = authenticator->checksum)) {
3280Sstevel@tonic-gate retval = KRB5KRB_AP_ERR_INAPP_CKSUM;
3290Sstevel@tonic-gate goto cleanup_authenticator;
3300Sstevel@tonic-gate }
3310Sstevel@tonic-gate
3320Sstevel@tonic-gate /* make sure the client is of proper lineage (see above) */
3330Sstevel@tonic-gate if (foreign_server) {
3340Sstevel@tonic-gate krb5_data *tkt_realm = krb5_princ_realm(kdc_context,
3350Sstevel@tonic-gate (*ticket)->enc_part2->client);
3360Sstevel@tonic-gate krb5_data *tgs_realm = krb5_princ_realm(kdc_context, tgs_server);
3370Sstevel@tonic-gate if (tkt_realm->length == tgs_realm->length &&
3380Sstevel@tonic-gate !memcmp(tkt_realm->data, tgs_realm->data, tgs_realm->length)) {
3390Sstevel@tonic-gate /* someone in a foreign realm claiming to be local */
3400Sstevel@tonic-gate krb5_klog_syslog(LOG_INFO, "PROCESS_TGS: failed lineage check");
3410Sstevel@tonic-gate retval = KRB5KDC_ERR_POLICY;
3420Sstevel@tonic-gate goto cleanup_authenticator;
3430Sstevel@tonic-gate }
3440Sstevel@tonic-gate }
3450Sstevel@tonic-gate
3460Sstevel@tonic-gate /*
3470Sstevel@tonic-gate * Check application checksum vs. tgs request
3480Sstevel@tonic-gate *
3490Sstevel@tonic-gate * We try checksumming the req-body two different ways: first we
3500Sstevel@tonic-gate * try reaching into the raw asn.1 stream (if available), and
3510Sstevel@tonic-gate * checksum that directly; if that fails, then we try encoding
3520Sstevel@tonic-gate * using our local asn.1 library.
3530Sstevel@tonic-gate */
3540Sstevel@tonic-gate if (pkt && (fetch_asn1_field((unsigned char *) pkt->data,
3550Sstevel@tonic-gate 1, 4, &scratch1) >= 0)) {
3560Sstevel@tonic-gate if (comp_cksum(kdc_context, &scratch1, *ticket, his_cksum)) {
3570Sstevel@tonic-gate if (!(retval = encode_krb5_kdc_req_body(request, &scratch)))
3580Sstevel@tonic-gate retval = comp_cksum(kdc_context, scratch, *ticket, his_cksum);
3590Sstevel@tonic-gate krb5_free_data(kdc_context, scratch);
3600Sstevel@tonic-gate }
3610Sstevel@tonic-gate }
3620Sstevel@tonic-gate
3630Sstevel@tonic-gate cleanup_authenticator:
3640Sstevel@tonic-gate krb5_free_authenticator(kdc_context, authenticator);
3650Sstevel@tonic-gate
3660Sstevel@tonic-gate cleanup_auth_context:
3670Sstevel@tonic-gate /* We do not want the free of the auth_context to close the rcache */
3680Sstevel@tonic-gate #ifdef USE_RCACHE
3690Sstevel@tonic-gate (void) krb5_auth_con_setrcache(kdc_context, auth_context, 0);
3700Sstevel@tonic-gate #endif
3710Sstevel@tonic-gate krb5_auth_con_free(kdc_context, auth_context);
3720Sstevel@tonic-gate
3730Sstevel@tonic-gate cleanup:
3740Sstevel@tonic-gate krb5_free_ap_req(kdc_context, apreq);
3750Sstevel@tonic-gate return retval;
3760Sstevel@tonic-gate }
3770Sstevel@tonic-gate
3780Sstevel@tonic-gate /* XXX This function should no longer be necessary.
3790Sstevel@tonic-gate * The KDC should take the keytab associated with the realm and pass that to
3800Sstevel@tonic-gate * the krb5_rd_req_decode(). --proven
3810Sstevel@tonic-gate *
3820Sstevel@tonic-gate * It's actually still used by do_tgs_req() for u2u auth, and not too
3830Sstevel@tonic-gate * much else. -- tlyu
3840Sstevel@tonic-gate */
3850Sstevel@tonic-gate krb5_error_code
kdc_get_server_key(krb5_ticket * ticket,krb5_keyblock ** key,krb5_kvno * kvno)3862881Smp153739 kdc_get_server_key(krb5_ticket *ticket, krb5_keyblock **key, krb5_kvno *kvno)
3870Sstevel@tonic-gate {
3880Sstevel@tonic-gate krb5_error_code retval;
3890Sstevel@tonic-gate krb5_db_entry server;
3900Sstevel@tonic-gate krb5_boolean more;
3910Sstevel@tonic-gate int nprincs;
3920Sstevel@tonic-gate krb5_key_data * server_key;
3930Sstevel@tonic-gate
3940Sstevel@tonic-gate nprincs = 1;
3950Sstevel@tonic-gate
3960Sstevel@tonic-gate if ((retval = krb5_db_get_principal(kdc_context, ticket->server,
3970Sstevel@tonic-gate &server, &nprincs,
3980Sstevel@tonic-gate &more))) {
3990Sstevel@tonic-gate return(retval);
4000Sstevel@tonic-gate }
4010Sstevel@tonic-gate if (more) {
4020Sstevel@tonic-gate krb5_db_free_principal(kdc_context, &server, nprincs);
4030Sstevel@tonic-gate return(KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE);
4040Sstevel@tonic-gate } else if (nprincs != 1) {
4050Sstevel@tonic-gate char *sname;
4060Sstevel@tonic-gate
4070Sstevel@tonic-gate krb5_db_free_principal(kdc_context, &server, nprincs);
4080Sstevel@tonic-gate if (!krb5_unparse_name(kdc_context, ticket->server, &sname)) {
4093998Ssemery limit_string(sname);
4100Sstevel@tonic-gate krb5_klog_syslog(LOG_ERR,"TGS_REQ: UNKNOWN SERVER: server='%s'",
4110Sstevel@tonic-gate sname);
4120Sstevel@tonic-gate free(sname);
4130Sstevel@tonic-gate }
4140Sstevel@tonic-gate return(KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN);
4150Sstevel@tonic-gate }
4160Sstevel@tonic-gate retval = krb5_dbe_find_enctype(kdc_context, &server,
4170Sstevel@tonic-gate ticket->enc_part.enctype, -1,
4180Sstevel@tonic-gate ticket->enc_part.kvno, &server_key);
4190Sstevel@tonic-gate if (retval)
4200Sstevel@tonic-gate goto errout;
4210Sstevel@tonic-gate if (!server_key) {
4220Sstevel@tonic-gate retval = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
4230Sstevel@tonic-gate goto errout;
4240Sstevel@tonic-gate }
4250Sstevel@tonic-gate *kvno = server_key->key_data_kvno;
4260Sstevel@tonic-gate if ((*key = (krb5_keyblock *)malloc(sizeof **key))) {
4270Sstevel@tonic-gate retval = krb5_dbekd_decrypt_key_data(kdc_context, &master_keyblock,
4280Sstevel@tonic-gate server_key,
4290Sstevel@tonic-gate *key, NULL);
4300Sstevel@tonic-gate } else
4310Sstevel@tonic-gate retval = ENOMEM;
4320Sstevel@tonic-gate errout:
4330Sstevel@tonic-gate krb5_db_free_principal(kdc_context, &server, nprincs);
4340Sstevel@tonic-gate return retval;
4350Sstevel@tonic-gate }
4360Sstevel@tonic-gate
4370Sstevel@tonic-gate /* This probably wants to be updated if you support last_req stuff */
4380Sstevel@tonic-gate
4390Sstevel@tonic-gate static krb5_last_req_entry nolrentry = { KV5M_LAST_REQ_ENTRY, KRB5_LRQ_NONE, 0 };
4400Sstevel@tonic-gate static krb5_last_req_entry *nolrarray[] = { &nolrentry, 0 };
4410Sstevel@tonic-gate
4420Sstevel@tonic-gate krb5_error_code
fetch_last_req_info(krb5_db_entry * dbentry,krb5_last_req_entry *** lrentry)4432881Smp153739 fetch_last_req_info(krb5_db_entry *dbentry, krb5_last_req_entry ***lrentry)
4440Sstevel@tonic-gate {
4450Sstevel@tonic-gate *lrentry = nolrarray;
4460Sstevel@tonic-gate return 0;
4470Sstevel@tonic-gate }
4480Sstevel@tonic-gate
4490Sstevel@tonic-gate
4500Sstevel@tonic-gate /* XXX! This is a temporary place-holder */
4510Sstevel@tonic-gate
4520Sstevel@tonic-gate krb5_error_code
check_hot_list(krb5_ticket * ticket)4532881Smp153739 check_hot_list(krb5_ticket *ticket)
4540Sstevel@tonic-gate {
4550Sstevel@tonic-gate return 0;
4560Sstevel@tonic-gate }
4570Sstevel@tonic-gate
4580Sstevel@tonic-gate
4590Sstevel@tonic-gate #define MAX_REALM_LN 500
4600Sstevel@tonic-gate
4610Sstevel@tonic-gate
4620Sstevel@tonic-gate /*
4630Sstevel@tonic-gate * subrealm - determine if r2 is a subrealm of r1
4640Sstevel@tonic-gate *
4650Sstevel@tonic-gate * SUBREALM takes two realms, r1 and r2, and
4660Sstevel@tonic-gate * determines if r2 is a subrealm of r1.
4670Sstevel@tonic-gate * r2 is a subrealm of r1 if (r1 is a prefix
4680Sstevel@tonic-gate * of r2 AND r1 and r2 begin with a /) or if
4690Sstevel@tonic-gate * (r1 is a suffix of r2 and neither r1 nor r2
4700Sstevel@tonic-gate * begin with a /).
4710Sstevel@tonic-gate *
4720Sstevel@tonic-gate * RETURNS: If r2 is a subrealm, and r1 is a prefix, the number
4730Sstevel@tonic-gate * of characters in the suffix of r2 is returned as a
4740Sstevel@tonic-gate * negative number.
4750Sstevel@tonic-gate *
4760Sstevel@tonic-gate * If r2 is a subrealm, and r1 is a suffix, the number
4770Sstevel@tonic-gate * of characters in the prefix of r2 is returned as a
4780Sstevel@tonic-gate * positive number.
4790Sstevel@tonic-gate *
4800Sstevel@tonic-gate * If r2 is not a subrealm, SUBREALM returns 0.
4810Sstevel@tonic-gate */
4820Sstevel@tonic-gate static int
subrealm(char * r1,char * r2)4832881Smp153739 subrealm(char *r1, char *r2)
4840Sstevel@tonic-gate {
4852881Smp153739 size_t l1,l2;
4860Sstevel@tonic-gate l1 = strlen(r1);
4870Sstevel@tonic-gate l2 = strlen(r2);
4880Sstevel@tonic-gate if(l2 <= l1) return(0);
4890Sstevel@tonic-gate if((*r1 == '/') && (*r2 == '/') && (strncmp(r1,r2,l1) == 0)) return(l1-l2);
4900Sstevel@tonic-gate if((*r1 != '/') && (*r2 != '/') && (strncmp(r1,r2+l2-l1,l1) == 0))
4910Sstevel@tonic-gate return(l2-l1);
4920Sstevel@tonic-gate return(0);
4930Sstevel@tonic-gate }
4940Sstevel@tonic-gate
4950Sstevel@tonic-gate /*
4960Sstevel@tonic-gate * add_to_transited Adds the name of the realm which issued the
4970Sstevel@tonic-gate * ticket granting ticket on which the new ticket to
4980Sstevel@tonic-gate * be issued is based (note that this is the same as
4990Sstevel@tonic-gate * the realm of the server listed in the ticket
5000Sstevel@tonic-gate * granting ticket.
5010Sstevel@tonic-gate *
5020Sstevel@tonic-gate * ASSUMPTIONS: This procedure assumes that the transited field from
5030Sstevel@tonic-gate * the existing ticket granting ticket already appears
5040Sstevel@tonic-gate * in compressed form. It will add the new realm while
5050Sstevel@tonic-gate * maintaining that form. As long as each successive
5060Sstevel@tonic-gate * realm is added using this (or a similar) routine, the
5070Sstevel@tonic-gate * transited field will be in compressed form. The
5080Sstevel@tonic-gate * basis step is an empty transited field which is, by
5090Sstevel@tonic-gate * its nature, in its most compressed form.
5100Sstevel@tonic-gate *
5110Sstevel@tonic-gate * ARGUMENTS: krb5_data *tgt_trans Transited field from TGT
5120Sstevel@tonic-gate * krb5_data *new_trans The transited field for the new ticket
5130Sstevel@tonic-gate * krb5_principal tgs Name of ticket granting server
5140Sstevel@tonic-gate * This includes the realm of the KDC
5150Sstevel@tonic-gate * that issued the ticket granting
5160Sstevel@tonic-gate * ticket. This is the realm that is
5170Sstevel@tonic-gate * to be added to the transited field.
5180Sstevel@tonic-gate * krb5_principal client Name of the client
5190Sstevel@tonic-gate * krb5_principal server The name of the requested server.
5200Sstevel@tonic-gate * This may be the an intermediate
5210Sstevel@tonic-gate * ticket granting server.
5220Sstevel@tonic-gate *
5230Sstevel@tonic-gate * The last two argument are needed since they are
5240Sstevel@tonic-gate * implicitly part of the transited field of the new ticket
5250Sstevel@tonic-gate * even though they are not explicitly listed.
5260Sstevel@tonic-gate *
5270Sstevel@tonic-gate * RETURNS: krb5_error_code - Success, or out of memory
5280Sstevel@tonic-gate *
5290Sstevel@tonic-gate * MODIFIES: new_trans: ->length will contain the length of the new
5300Sstevel@tonic-gate * transited field.
5310Sstevel@tonic-gate *
5320Sstevel@tonic-gate * If ->data was not null when this procedure
5330Sstevel@tonic-gate * is called, the memory referenced by ->data
5340Sstevel@tonic-gate * will be deallocated.
5350Sstevel@tonic-gate *
5360Sstevel@tonic-gate * Memory will be allocated for the new transited field
5370Sstevel@tonic-gate * ->data will be updated to point to the newly
5380Sstevel@tonic-gate * allocated memory.
5390Sstevel@tonic-gate *
5400Sstevel@tonic-gate * BUGS: The space allocated for the new transited field is the
5410Sstevel@tonic-gate * maximum that might be needed given the old transited field,
5420Sstevel@tonic-gate * and the realm to be added. This length is calculated
5430Sstevel@tonic-gate * assuming that no compression of the new realm is possible.
5440Sstevel@tonic-gate * This has no adverse consequences other than the allocation
5450Sstevel@tonic-gate * of more space than required.
5460Sstevel@tonic-gate *
5470Sstevel@tonic-gate * This procedure will not yet use the null subfield notation,
5480Sstevel@tonic-gate * and it will get confused if it sees it.
5490Sstevel@tonic-gate *
5500Sstevel@tonic-gate * This procedure does not check for quoted commas in realm
5510Sstevel@tonic-gate * names.
5520Sstevel@tonic-gate */
5530Sstevel@tonic-gate
554*7934SMark.Phalan@Sun.COM static char *
data2string(krb5_data * d)555*7934SMark.Phalan@Sun.COM data2string (krb5_data *d)
556*7934SMark.Phalan@Sun.COM {
557*7934SMark.Phalan@Sun.COM char *s;
558*7934SMark.Phalan@Sun.COM s = malloc(d->length + 1);
559*7934SMark.Phalan@Sun.COM if (s) {
560*7934SMark.Phalan@Sun.COM memcpy(s, d->data, d->length);
561*7934SMark.Phalan@Sun.COM s[d->length] = 0;
562*7934SMark.Phalan@Sun.COM }
563*7934SMark.Phalan@Sun.COM return s;
564*7934SMark.Phalan@Sun.COM }
565*7934SMark.Phalan@Sun.COM
5660Sstevel@tonic-gate krb5_error_code
add_to_transited(krb5_data * tgt_trans,krb5_data * new_trans,krb5_principal tgs,krb5_principal client,krb5_principal server)5672881Smp153739 add_to_transited(krb5_data *tgt_trans, krb5_data *new_trans,
5682881Smp153739 krb5_principal tgs, krb5_principal client,
5692881Smp153739 krb5_principal server)
5700Sstevel@tonic-gate {
5710Sstevel@tonic-gate krb5_error_code retval;
5720Sstevel@tonic-gate char *realm;
5730Sstevel@tonic-gate char *trans;
5740Sstevel@tonic-gate char *otrans, *otrans_ptr;
5750Sstevel@tonic-gate
5760Sstevel@tonic-gate /* The following are for stepping through the transited field */
5770Sstevel@tonic-gate
5780Sstevel@tonic-gate char prev[MAX_REALM_LN];
5790Sstevel@tonic-gate char next[MAX_REALM_LN];
5800Sstevel@tonic-gate char current[MAX_REALM_LN];
5810Sstevel@tonic-gate char exp[MAX_REALM_LN]; /* Expanded current realm name */
5820Sstevel@tonic-gate
5830Sstevel@tonic-gate int i;
5840Sstevel@tonic-gate int clst, nlst; /* count of last character in current and next */
5850Sstevel@tonic-gate int pl, pl1; /* prefix length */
5860Sstevel@tonic-gate int added; /* TRUE = new realm has been added */
5870Sstevel@tonic-gate
588*7934SMark.Phalan@Sun.COM realm = data2string(krb5_princ_realm(kdc_context, tgs));
589*7934SMark.Phalan@Sun.COM if (realm == NULL)
590*7934SMark.Phalan@Sun.COM return(ENOMEM);
5910Sstevel@tonic-gate
592*7934SMark.Phalan@Sun.COM otrans = data2string(tgt_trans);
593*7934SMark.Phalan@Sun.COM if (otrans == NULL) {
594*7934SMark.Phalan@Sun.COM free(realm);
595*7934SMark.Phalan@Sun.COM return(ENOMEM);
5960Sstevel@tonic-gate }
5970Sstevel@tonic-gate /* Keep track of start so we can free */
5980Sstevel@tonic-gate otrans_ptr = otrans;
5990Sstevel@tonic-gate
6000Sstevel@tonic-gate /* +1 for null,
6010Sstevel@tonic-gate +1 for extra comma which may be added between
6020Sstevel@tonic-gate +1 for potential space when leading slash in realm */
6030Sstevel@tonic-gate if (!(trans = (char *) malloc(strlen(realm) + strlen(otrans) + 3))) {
6040Sstevel@tonic-gate retval = ENOMEM;
6050Sstevel@tonic-gate goto fail;
6060Sstevel@tonic-gate }
6070Sstevel@tonic-gate
6080Sstevel@tonic-gate if (new_trans->data) free(new_trans->data);
6090Sstevel@tonic-gate new_trans->data = trans;
6100Sstevel@tonic-gate new_trans->length = 0;
6110Sstevel@tonic-gate
6120Sstevel@tonic-gate trans[0] = '\0';
6130Sstevel@tonic-gate
6140Sstevel@tonic-gate /* For the purpose of appending, the realm preceding the first */
6150Sstevel@tonic-gate /* realm in the transited field is considered the null realm */
6160Sstevel@tonic-gate
6170Sstevel@tonic-gate prev[0] = '\0';
6180Sstevel@tonic-gate
6190Sstevel@tonic-gate /* read field into current */
6200Sstevel@tonic-gate for (i = 0; *otrans != '\0';) {
6212881Smp153739 if (*otrans == '\\') {
6222881Smp153739 if (*(++otrans) == '\0')
6232881Smp153739 break;
6242881Smp153739 else
6252881Smp153739 continue;
6262881Smp153739 }
6272881Smp153739 if (*otrans == ',') {
6282881Smp153739 otrans++;
6292881Smp153739 break;
6302881Smp153739 }
6312881Smp153739 current[i++] = *otrans++;
6322881Smp153739 if (i >= MAX_REALM_LN) {
6332881Smp153739 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
6342881Smp153739 goto fail;
6352881Smp153739 }
6360Sstevel@tonic-gate }
6370Sstevel@tonic-gate current[i] = '\0';
6380Sstevel@tonic-gate
6390Sstevel@tonic-gate added = (krb5_princ_realm(kdc_context, client)->length == strlen(realm) &&
6400Sstevel@tonic-gate !strncmp(krb5_princ_realm(kdc_context, client)->data, realm, strlen(realm))) ||
6410Sstevel@tonic-gate (krb5_princ_realm(kdc_context, server)->length == strlen(realm) &&
6420Sstevel@tonic-gate !strncmp(krb5_princ_realm(kdc_context, server)->data, realm, strlen(realm)));
6430Sstevel@tonic-gate
6440Sstevel@tonic-gate while (current[0]) {
6450Sstevel@tonic-gate
6460Sstevel@tonic-gate /* figure out expanded form of current name */
6470Sstevel@tonic-gate
6480Sstevel@tonic-gate clst = strlen(current) - 1;
6490Sstevel@tonic-gate if (current[0] == ' ') {
6500Sstevel@tonic-gate strncpy(exp, current+1, sizeof(exp) - 1);
6510Sstevel@tonic-gate exp[sizeof(exp) - 1] = '\0';
6520Sstevel@tonic-gate }
6530Sstevel@tonic-gate else if ((current[0] == '/') && (prev[0] == '/')) {
6540Sstevel@tonic-gate strncpy(exp, prev, sizeof(exp) - 1);
6550Sstevel@tonic-gate exp[sizeof(exp) - 1] = '\0';
6560Sstevel@tonic-gate if (strlen(exp) + strlen(current) + 1 >= MAX_REALM_LN) {
6570Sstevel@tonic-gate retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
6580Sstevel@tonic-gate goto fail;
6590Sstevel@tonic-gate }
6600Sstevel@tonic-gate strncat(exp, current, sizeof(exp) - 1 - strlen(exp));
6610Sstevel@tonic-gate }
6620Sstevel@tonic-gate else if (current[clst] == '.') {
6630Sstevel@tonic-gate strncpy(exp, current, sizeof(exp) - 1);
6640Sstevel@tonic-gate exp[sizeof(exp) - 1] = '\0';
6650Sstevel@tonic-gate if (strlen(exp) + strlen(prev) + 1 >= MAX_REALM_LN) {
6660Sstevel@tonic-gate retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
6670Sstevel@tonic-gate goto fail;
6680Sstevel@tonic-gate }
6690Sstevel@tonic-gate strncat(exp, prev, sizeof(exp) - 1 - strlen(exp));
6700Sstevel@tonic-gate }
6710Sstevel@tonic-gate else {
6720Sstevel@tonic-gate strncpy(exp, current, sizeof(exp) - 1);
6730Sstevel@tonic-gate exp[sizeof(exp) - 1] = '\0';
6740Sstevel@tonic-gate }
6750Sstevel@tonic-gate
6760Sstevel@tonic-gate /* read field into next */
6770Sstevel@tonic-gate for (i = 0; *otrans != '\0';) {
6782881Smp153739 if (*otrans == '\\') {
6792881Smp153739 if (*(++otrans) == '\0')
6802881Smp153739 break;
6812881Smp153739 else
6822881Smp153739 continue;
6832881Smp153739 }
6842881Smp153739 if (*otrans == ',') {
6852881Smp153739 otrans++;
6862881Smp153739 break;
6872881Smp153739 }
6882881Smp153739 next[i++] = *otrans++;
6892881Smp153739 if (i >= MAX_REALM_LN) {
6902881Smp153739 retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
6912881Smp153739 goto fail;
6922881Smp153739 }
6930Sstevel@tonic-gate }
6940Sstevel@tonic-gate next[i] = '\0';
6950Sstevel@tonic-gate nlst = i - 1;
6960Sstevel@tonic-gate
6970Sstevel@tonic-gate if (!strcmp(exp, realm)) added = TRUE;
6980Sstevel@tonic-gate
6990Sstevel@tonic-gate /* If we still have to insert the new realm */
7000Sstevel@tonic-gate
7010Sstevel@tonic-gate if (!added) {
7020Sstevel@tonic-gate
7030Sstevel@tonic-gate /* Is the next field compressed? If not, and if the new */
7040Sstevel@tonic-gate /* realm is a subrealm of the current realm, compress */
7050Sstevel@tonic-gate /* the new realm, and insert immediately following the */
7060Sstevel@tonic-gate /* current one. Note that we can not do this if the next*/
7070Sstevel@tonic-gate /* field is already compressed since it would mess up */
7080Sstevel@tonic-gate /* what has already been done. In most cases, this is */
7090Sstevel@tonic-gate /* not a problem because the realm to be added will be a */
7100Sstevel@tonic-gate /* subrealm of the next field too, and we will catch */
7110Sstevel@tonic-gate /* it in a future iteration. */
7120Sstevel@tonic-gate
713*7934SMark.Phalan@Sun.COM /* Note that the second test here is an unsigned comparison,
714*7934SMark.Phalan@Sun.COM so the first half (or a cast) is also required. */
715*7934SMark.Phalan@Sun.COM assert(nlst < 0 || nlst < sizeof(next));
716*7934SMark.Phalan@Sun.COM if ((nlst < 0 || next[nlst] != '.') &&
717*7934SMark.Phalan@Sun.COM (next[0] != '/') &&
718*7934SMark.Phalan@Sun.COM (pl = subrealm(exp, realm))) {
7190Sstevel@tonic-gate added = TRUE;
7200Sstevel@tonic-gate current[sizeof(current) - 1] = '\0';
7210Sstevel@tonic-gate if (strlen(current) + (pl>0?pl:-pl) + 2 >= MAX_REALM_LN) {
7220Sstevel@tonic-gate retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
7230Sstevel@tonic-gate goto fail;
7240Sstevel@tonic-gate }
7250Sstevel@tonic-gate strncat(current, ",", sizeof(current) - 1 - strlen(current));
7260Sstevel@tonic-gate if (pl > 0) {
7272881Smp153739 strncat(current, realm, (unsigned) pl);
7280Sstevel@tonic-gate }
7290Sstevel@tonic-gate else {
7302881Smp153739 strncat(current, realm+strlen(realm)+pl, (unsigned) (-pl));
7310Sstevel@tonic-gate }
7320Sstevel@tonic-gate }
7330Sstevel@tonic-gate
7340Sstevel@tonic-gate /* Whether or not the next field is compressed, if the */
7350Sstevel@tonic-gate /* realm to be added is a superrealm of the current realm,*/
7360Sstevel@tonic-gate /* then the current realm can be compressed. First the */
7370Sstevel@tonic-gate /* realm to be added must be compressed relative to the */
7380Sstevel@tonic-gate /* previous realm (if possible), and then the current */
7390Sstevel@tonic-gate /* realm compressed relative to the new realm. Note that */
7400Sstevel@tonic-gate /* if the realm to be added is also a superrealm of the */
7410Sstevel@tonic-gate /* previous realm, it would have been added earlier, and */
7420Sstevel@tonic-gate /* we would not reach this step this time around. */
7430Sstevel@tonic-gate
7440Sstevel@tonic-gate else if ((pl = subrealm(realm, exp))) {
7450Sstevel@tonic-gate added = TRUE;
7460Sstevel@tonic-gate current[0] = '\0';
7470Sstevel@tonic-gate if ((pl1 = subrealm(prev,realm))) {
7480Sstevel@tonic-gate if (strlen(current) + (pl1>0?pl1:-pl1) + 1 >= MAX_REALM_LN) {
7490Sstevel@tonic-gate retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
7500Sstevel@tonic-gate goto fail;
7510Sstevel@tonic-gate }
7520Sstevel@tonic-gate if (pl1 > 0) {
7532881Smp153739 strncat(current, realm, (unsigned) pl1);
7540Sstevel@tonic-gate }
7550Sstevel@tonic-gate else {
7562881Smp153739 strncat(current, realm+strlen(realm)+pl1, (unsigned) (-pl1));
7570Sstevel@tonic-gate }
7580Sstevel@tonic-gate }
7590Sstevel@tonic-gate else { /* If not a subrealm */
7600Sstevel@tonic-gate if ((realm[0] == '/') && prev[0]) {
7610Sstevel@tonic-gate if (strlen(current) + 2 >= MAX_REALM_LN) {
7620Sstevel@tonic-gate retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
7630Sstevel@tonic-gate goto fail;
7640Sstevel@tonic-gate }
7650Sstevel@tonic-gate strncat(current, " ", sizeof(current) - 1 - strlen(current));
7660Sstevel@tonic-gate current[sizeof(current) - 1] = '\0';
7670Sstevel@tonic-gate }
7680Sstevel@tonic-gate if (strlen(current) + strlen(realm) + 1 >= MAX_REALM_LN) {
7690Sstevel@tonic-gate retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
7700Sstevel@tonic-gate goto fail;
7710Sstevel@tonic-gate }
7720Sstevel@tonic-gate strncat(current, realm, sizeof(current) - 1 - strlen(current));
7730Sstevel@tonic-gate current[sizeof(current) - 1] = '\0';
7740Sstevel@tonic-gate }
7750Sstevel@tonic-gate if (strlen(current) + (pl>0?pl:-pl) + 2 >= MAX_REALM_LN) {
7760Sstevel@tonic-gate retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
7770Sstevel@tonic-gate goto fail;
7780Sstevel@tonic-gate }
7790Sstevel@tonic-gate strncat(current,",", sizeof(current) - 1 - strlen(current));
7800Sstevel@tonic-gate current[sizeof(current) - 1] = '\0';
7810Sstevel@tonic-gate if (pl > 0) {
7822881Smp153739 strncat(current, exp, (unsigned) pl);
7830Sstevel@tonic-gate }
7840Sstevel@tonic-gate else {
7852881Smp153739 strncat(current, exp+strlen(exp)+pl, (unsigned)(-pl));
7860Sstevel@tonic-gate }
7870Sstevel@tonic-gate }
7880Sstevel@tonic-gate }
7890Sstevel@tonic-gate
7900Sstevel@tonic-gate if (new_trans->length != 0) {
7910Sstevel@tonic-gate if (strlen(trans) + 2 >= MAX_REALM_LN) {
7920Sstevel@tonic-gate retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
7930Sstevel@tonic-gate goto fail;
7940Sstevel@tonic-gate }
7950Sstevel@tonic-gate strcat(trans, ",");
7960Sstevel@tonic-gate }
7970Sstevel@tonic-gate if (strlen(trans) + strlen(current) + 1 >= MAX_REALM_LN) {
7980Sstevel@tonic-gate retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
7990Sstevel@tonic-gate goto fail;
8000Sstevel@tonic-gate }
8010Sstevel@tonic-gate strcat(trans, current);
8020Sstevel@tonic-gate new_trans->length = strlen(trans);
8030Sstevel@tonic-gate
8040Sstevel@tonic-gate strncpy(prev, exp, sizeof(prev) - 1);
8050Sstevel@tonic-gate prev[sizeof(prev) - 1] = '\0';
8060Sstevel@tonic-gate strncpy(current, next, sizeof(current) - 1);
8070Sstevel@tonic-gate current[sizeof(current) - 1] = '\0';
8080Sstevel@tonic-gate }
8090Sstevel@tonic-gate
8100Sstevel@tonic-gate if (!added) {
8110Sstevel@tonic-gate if (new_trans->length != 0) {
8120Sstevel@tonic-gate if (strlen(trans) + 2 >= MAX_REALM_LN) {
8130Sstevel@tonic-gate retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
8140Sstevel@tonic-gate goto fail;
8150Sstevel@tonic-gate }
8160Sstevel@tonic-gate strcat(trans, ",");
8170Sstevel@tonic-gate }
8180Sstevel@tonic-gate if((realm[0] == '/') && trans[0]) {
8190Sstevel@tonic-gate if (strlen(trans) + 2 >= MAX_REALM_LN) {
8200Sstevel@tonic-gate retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
8210Sstevel@tonic-gate goto fail;
8220Sstevel@tonic-gate }
8230Sstevel@tonic-gate strcat(trans, " ");
8240Sstevel@tonic-gate }
8250Sstevel@tonic-gate if (strlen(trans) + strlen(realm) + 1 >= MAX_REALM_LN) {
8260Sstevel@tonic-gate retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
8270Sstevel@tonic-gate goto fail;
8280Sstevel@tonic-gate }
8290Sstevel@tonic-gate strcat(trans, realm);
8300Sstevel@tonic-gate new_trans->length = strlen(trans);
8310Sstevel@tonic-gate }
8320Sstevel@tonic-gate
8330Sstevel@tonic-gate retval = 0;
8340Sstevel@tonic-gate fail:
8350Sstevel@tonic-gate free(realm);
8360Sstevel@tonic-gate free(otrans_ptr);
8370Sstevel@tonic-gate return (retval);
8380Sstevel@tonic-gate }
8390Sstevel@tonic-gate
8400Sstevel@tonic-gate /*
8410Sstevel@tonic-gate * Routines that validate a AS request; checks a lot of things. :-)
8420Sstevel@tonic-gate *
8430Sstevel@tonic-gate * Returns a Kerberos protocol error number, which is _not_ the same
8440Sstevel@tonic-gate * as a com_err error number!
8450Sstevel@tonic-gate */
8460Sstevel@tonic-gate #define AS_INVALID_OPTIONS (KDC_OPT_FORWARDED | KDC_OPT_PROXY |\
8472881Smp153739 KDC_OPT_VALIDATE | KDC_OPT_RENEW | KDC_OPT_ENC_TKT_IN_SKEY)
8480Sstevel@tonic-gate int
validate_as_request(register krb5_kdc_req * request,krb5_db_entry client,krb5_db_entry server,krb5_timestamp kdc_time,const char ** status)8492881Smp153739 validate_as_request(register krb5_kdc_req *request, krb5_db_entry client,
8502881Smp153739 krb5_db_entry server, krb5_timestamp kdc_time,
8512881Smp153739 const char **status)
8520Sstevel@tonic-gate {
8530Sstevel@tonic-gate int errcode;
8540Sstevel@tonic-gate
8550Sstevel@tonic-gate /*
8562881Smp153739 * If an option is set that is only allowed in TGS requests, complain.
8570Sstevel@tonic-gate */
8580Sstevel@tonic-gate if (request->kdc_options & AS_INVALID_OPTIONS) {
8590Sstevel@tonic-gate *status = "INVALID AS OPTIONS";
8600Sstevel@tonic-gate return KDC_ERR_BADOPTION;
8610Sstevel@tonic-gate }
8620Sstevel@tonic-gate
8630Sstevel@tonic-gate /* The client's password must not be expired, unless the server is
8640Sstevel@tonic-gate a KRB5_KDC_PWCHANGE_SERVICE. */
8650Sstevel@tonic-gate if (client.pw_expiration && client.pw_expiration < kdc_time &&
8660Sstevel@tonic-gate !isflagset(server.attributes, KRB5_KDB_PWCHANGE_SERVICE)) {
8670Sstevel@tonic-gate *status = "CLIENT KEY EXPIRED";
8680Sstevel@tonic-gate #ifdef KRBCONF_VAGUE_ERRORS
8690Sstevel@tonic-gate return(KRB_ERR_GENERIC);
8700Sstevel@tonic-gate #else
8710Sstevel@tonic-gate return(KDC_ERR_KEY_EXP);
8720Sstevel@tonic-gate #endif
8730Sstevel@tonic-gate }
8740Sstevel@tonic-gate
8750Sstevel@tonic-gate /* The client must not be expired */
8760Sstevel@tonic-gate if (client.expiration && client.expiration < kdc_time) {
8770Sstevel@tonic-gate *status = "CLIENT EXPIRED";
8780Sstevel@tonic-gate #ifdef KRBCONF_VAGUE_ERRORS
8790Sstevel@tonic-gate return(KRB_ERR_GENERIC);
8800Sstevel@tonic-gate #else
8810Sstevel@tonic-gate return(KDC_ERR_NAME_EXP);
8820Sstevel@tonic-gate #endif
8830Sstevel@tonic-gate }
8840Sstevel@tonic-gate
8850Sstevel@tonic-gate /* The server must not be expired */
8860Sstevel@tonic-gate if (server.expiration && server.expiration < kdc_time) {
8870Sstevel@tonic-gate *status = "SERVICE EXPIRED";
8880Sstevel@tonic-gate return(KDC_ERR_SERVICE_EXP);
8890Sstevel@tonic-gate }
8900Sstevel@tonic-gate
8910Sstevel@tonic-gate /*
8920Sstevel@tonic-gate * If the client requires password changing, then only allow the
8930Sstevel@tonic-gate * pwchange service.
8940Sstevel@tonic-gate */
8950Sstevel@tonic-gate if (isflagset(client.attributes, KRB5_KDB_REQUIRES_PWCHANGE) &&
8960Sstevel@tonic-gate !isflagset(server.attributes, KRB5_KDB_PWCHANGE_SERVICE)) {
8970Sstevel@tonic-gate *status = "REQUIRED PWCHANGE";
8980Sstevel@tonic-gate return(KDC_ERR_KEY_EXP);
8990Sstevel@tonic-gate }
9000Sstevel@tonic-gate
9010Sstevel@tonic-gate /* Client and server must allow postdating tickets */
9020Sstevel@tonic-gate if ((isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE) ||
9030Sstevel@tonic-gate isflagset(request->kdc_options, KDC_OPT_POSTDATED)) &&
9040Sstevel@tonic-gate (isflagset(client.attributes, KRB5_KDB_DISALLOW_POSTDATED) ||
9050Sstevel@tonic-gate isflagset(server.attributes, KRB5_KDB_DISALLOW_POSTDATED))) {
9060Sstevel@tonic-gate *status = "POSTDATE NOT ALLOWED";
9070Sstevel@tonic-gate return(KDC_ERR_CANNOT_POSTDATE);
9080Sstevel@tonic-gate }
9090Sstevel@tonic-gate
9100Sstevel@tonic-gate /* Client and server must allow forwardable tickets */
9110Sstevel@tonic-gate if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE) &&
9120Sstevel@tonic-gate (isflagset(client.attributes, KRB5_KDB_DISALLOW_FORWARDABLE) ||
9130Sstevel@tonic-gate isflagset(server.attributes, KRB5_KDB_DISALLOW_FORWARDABLE))) {
9140Sstevel@tonic-gate *status = "FORWARDABLE NOT ALLOWED";
9150Sstevel@tonic-gate return(KDC_ERR_POLICY);
9160Sstevel@tonic-gate }
9170Sstevel@tonic-gate
9180Sstevel@tonic-gate /* Client and server must allow renewable tickets */
9190Sstevel@tonic-gate if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE) &&
9200Sstevel@tonic-gate (isflagset(client.attributes, KRB5_KDB_DISALLOW_RENEWABLE) ||
9210Sstevel@tonic-gate isflagset(server.attributes, KRB5_KDB_DISALLOW_RENEWABLE))) {
9220Sstevel@tonic-gate *status = "RENEWABLE NOT ALLOWED";
9230Sstevel@tonic-gate return(KDC_ERR_POLICY);
9240Sstevel@tonic-gate }
9250Sstevel@tonic-gate
9260Sstevel@tonic-gate /* Client and server must allow proxiable tickets */
9270Sstevel@tonic-gate if (isflagset(request->kdc_options, KDC_OPT_PROXIABLE) &&
9280Sstevel@tonic-gate (isflagset(client.attributes, KRB5_KDB_DISALLOW_PROXIABLE) ||
9290Sstevel@tonic-gate isflagset(server.attributes, KRB5_KDB_DISALLOW_PROXIABLE))) {
9300Sstevel@tonic-gate *status = "PROXIABLE NOT ALLOWED";
9310Sstevel@tonic-gate return(KDC_ERR_POLICY);
9320Sstevel@tonic-gate }
9330Sstevel@tonic-gate
9340Sstevel@tonic-gate /* Check to see if client is locked out */
9350Sstevel@tonic-gate if (isflagset(client.attributes, KRB5_KDB_DISALLOW_ALL_TIX)) {
9360Sstevel@tonic-gate *status = "CLIENT LOCKED OUT";
9370Sstevel@tonic-gate return(KDC_ERR_C_PRINCIPAL_UNKNOWN);
9380Sstevel@tonic-gate }
9390Sstevel@tonic-gate
9400Sstevel@tonic-gate /* Check to see if server is locked out */
9410Sstevel@tonic-gate if (isflagset(server.attributes, KRB5_KDB_DISALLOW_ALL_TIX)) {
9420Sstevel@tonic-gate *status = "SERVICE LOCKED OUT";
9430Sstevel@tonic-gate return(KDC_ERR_S_PRINCIPAL_UNKNOWN);
9440Sstevel@tonic-gate }
9450Sstevel@tonic-gate
9460Sstevel@tonic-gate /* Check to see if server is allowed to be a service */
9470Sstevel@tonic-gate if (isflagset(server.attributes, KRB5_KDB_DISALLOW_SVR)) {
9480Sstevel@tonic-gate *status = "SERVICE NOT ALLOWED";
9490Sstevel@tonic-gate return(KDC_ERR_S_PRINCIPAL_UNKNOWN);
9500Sstevel@tonic-gate }
9510Sstevel@tonic-gate
9520Sstevel@tonic-gate /*
9530Sstevel@tonic-gate * Check against local policy
9540Sstevel@tonic-gate */
9550Sstevel@tonic-gate errcode = against_local_policy_as(request, server, client,
9560Sstevel@tonic-gate kdc_time, status);
9570Sstevel@tonic-gate if (errcode)
9580Sstevel@tonic-gate return errcode;
9590Sstevel@tonic-gate
9600Sstevel@tonic-gate return 0;
9610Sstevel@tonic-gate }
9620Sstevel@tonic-gate
9630Sstevel@tonic-gate #define ASN1_ID_CLASS (0xc0)
9640Sstevel@tonic-gate #define ASN1_ID_TYPE (0x20)
9650Sstevel@tonic-gate #define ASN1_ID_TAG (0x1f)
9660Sstevel@tonic-gate #define ASN1_CLASS_UNIV (0)
9670Sstevel@tonic-gate #define ASN1_CLASS_APP (1)
9680Sstevel@tonic-gate #define ASN1_CLASS_CTX (2)
9690Sstevel@tonic-gate #define ASN1_CLASS_PRIV (3)
9700Sstevel@tonic-gate #define asn1_id_constructed(x) (x & ASN1_ID_TYPE)
9710Sstevel@tonic-gate #define asn1_id_primitive(x) (!asn1_id_constructed(x))
9720Sstevel@tonic-gate #define asn1_id_class(x) ((x & ASN1_ID_CLASS) >> 6)
9730Sstevel@tonic-gate #define asn1_id_tag(x) (x & ASN1_ID_TAG)
9740Sstevel@tonic-gate
9750Sstevel@tonic-gate /*
9760Sstevel@tonic-gate * asn1length - return encoded length of value.
9770Sstevel@tonic-gate *
9780Sstevel@tonic-gate * passed a pointer into the asn.1 stream, which is updated
9790Sstevel@tonic-gate * to point right after the length bits.
9800Sstevel@tonic-gate *
9810Sstevel@tonic-gate * returns -1 on failure.
9820Sstevel@tonic-gate */
9830Sstevel@tonic-gate static int
asn1length(unsigned char ** astream)9842881Smp153739 asn1length(unsigned char **astream)
9850Sstevel@tonic-gate {
9860Sstevel@tonic-gate int length; /* resulting length */
9870Sstevel@tonic-gate int sublen; /* sublengths */
9880Sstevel@tonic-gate int blen; /* bytes of length */
9890Sstevel@tonic-gate unsigned char *p; /* substring searching */
9900Sstevel@tonic-gate
9910Sstevel@tonic-gate if (**astream & 0x80) {
9920Sstevel@tonic-gate blen = **astream & 0x7f;
9930Sstevel@tonic-gate if (blen > 3) {
9940Sstevel@tonic-gate return(-1);
9950Sstevel@tonic-gate }
9960Sstevel@tonic-gate for (++*astream, length = 0; blen; ++*astream, blen--) {
9970Sstevel@tonic-gate length = (length << 8) | **astream;
9980Sstevel@tonic-gate }
9990Sstevel@tonic-gate if (length == 0) {
10000Sstevel@tonic-gate /* indefinite length, figure out by hand */
10010Sstevel@tonic-gate p = *astream;
10020Sstevel@tonic-gate p++;
10030Sstevel@tonic-gate while (1) {
10040Sstevel@tonic-gate /* compute value length. */
10050Sstevel@tonic-gate if ((sublen = asn1length(&p)) < 0) {
10060Sstevel@tonic-gate return(-1);
10070Sstevel@tonic-gate }
10080Sstevel@tonic-gate p += sublen;
10090Sstevel@tonic-gate /* check for termination */
10100Sstevel@tonic-gate if ((!*p++) && (!*p)) {
10110Sstevel@tonic-gate p++;
10120Sstevel@tonic-gate break;
10130Sstevel@tonic-gate }
10140Sstevel@tonic-gate }
10150Sstevel@tonic-gate length = p - *astream;
10160Sstevel@tonic-gate }
10170Sstevel@tonic-gate } else {
10180Sstevel@tonic-gate length = **astream;
10190Sstevel@tonic-gate ++*astream;
10200Sstevel@tonic-gate }
10210Sstevel@tonic-gate return(length);
10220Sstevel@tonic-gate }
10230Sstevel@tonic-gate
10240Sstevel@tonic-gate /*
10250Sstevel@tonic-gate * fetch_asn1_field - return raw asn.1 stream of subfield.
10260Sstevel@tonic-gate *
10270Sstevel@tonic-gate * this routine is passed a context-dependent tag number and "level" and returns
10280Sstevel@tonic-gate * the size and length of the corresponding level subfield.
10290Sstevel@tonic-gate *
10300Sstevel@tonic-gate * levels and are numbered starting from 1.
10310Sstevel@tonic-gate *
10320Sstevel@tonic-gate * returns 0 on success, -1 otherwise.
10330Sstevel@tonic-gate */
10340Sstevel@tonic-gate int
fetch_asn1_field(unsigned char * astream,unsigned int level,unsigned int field,krb5_data * data)10352881Smp153739 fetch_asn1_field(unsigned char *astream, unsigned int level,
10362881Smp153739 unsigned int field, krb5_data *data)
10370Sstevel@tonic-gate {
10380Sstevel@tonic-gate unsigned char *estream; /* end of stream */
10390Sstevel@tonic-gate int classes; /* # classes seen so far this level */
10400Sstevel@tonic-gate unsigned int levels = 0; /* levels seen so far */
10410Sstevel@tonic-gate int lastlevel = 1000; /* last level seen */
10420Sstevel@tonic-gate int length; /* various lengths */
10430Sstevel@tonic-gate int tag; /* tag number */
10440Sstevel@tonic-gate unsigned char savelen; /* saved length of our field */
10450Sstevel@tonic-gate
10460Sstevel@tonic-gate classes = -1;
10470Sstevel@tonic-gate /* we assume that the first identifier/length will tell us
10480Sstevel@tonic-gate how long the entire stream is. */
10490Sstevel@tonic-gate astream++;
10500Sstevel@tonic-gate estream = astream;
10510Sstevel@tonic-gate if ((length = asn1length(&astream)) < 0) {
10520Sstevel@tonic-gate return(-1);
10530Sstevel@tonic-gate }
10540Sstevel@tonic-gate estream += length;
10550Sstevel@tonic-gate /* search down the stream, checking identifiers. we process identifiers
10560Sstevel@tonic-gate until we hit the "level" we want, and then process that level for our
10570Sstevel@tonic-gate subfield, always making sure we don't go off the end of the stream. */
10580Sstevel@tonic-gate while (astream < estream) {
10590Sstevel@tonic-gate if (!asn1_id_constructed(*astream)) {
10600Sstevel@tonic-gate return(-1);
10610Sstevel@tonic-gate }
10620Sstevel@tonic-gate if (asn1_id_class(*astream) == ASN1_CLASS_CTX) {
10630Sstevel@tonic-gate if ((tag = (int)asn1_id_tag(*astream)) <= lastlevel) {
10640Sstevel@tonic-gate levels++;
10650Sstevel@tonic-gate classes = -1;
10660Sstevel@tonic-gate }
10670Sstevel@tonic-gate lastlevel = tag;
10680Sstevel@tonic-gate if (levels == level) {
10690Sstevel@tonic-gate /* in our context-dependent class, is this the one we're looking for ? */
10700Sstevel@tonic-gate if (tag == field) {
10710Sstevel@tonic-gate /* return length and data */
10720Sstevel@tonic-gate astream++;
10730Sstevel@tonic-gate savelen = *astream;
10740Sstevel@tonic-gate if ((data->length = asn1length(&astream)) < 0) {
10750Sstevel@tonic-gate return(-1);
10760Sstevel@tonic-gate }
10770Sstevel@tonic-gate /* if the field length is indefinite, we will have to subtract two
10780Sstevel@tonic-gate (terminating octets) from the length returned since we don't want
10790Sstevel@tonic-gate to pass any info from the "wrapper" back. asn1length will always return
10800Sstevel@tonic-gate the *total* length of the field, not just what's contained in it */
10810Sstevel@tonic-gate if ((savelen & 0xff) == 0x80) {
10820Sstevel@tonic-gate data->length -=2 ;
10830Sstevel@tonic-gate }
10840Sstevel@tonic-gate data->data = (char *)astream;
10850Sstevel@tonic-gate return(0);
10860Sstevel@tonic-gate } else if (tag <= classes) {
10870Sstevel@tonic-gate /* we've seen this class before, something must be wrong */
10880Sstevel@tonic-gate return(-1);
10890Sstevel@tonic-gate } else {
10900Sstevel@tonic-gate classes = tag;
10910Sstevel@tonic-gate }
10920Sstevel@tonic-gate }
10930Sstevel@tonic-gate }
10940Sstevel@tonic-gate /* if we're not on our level yet, process this value. otherwise skip over it */
10950Sstevel@tonic-gate astream++;
10960Sstevel@tonic-gate if ((length = asn1length(&astream)) < 0) {
10970Sstevel@tonic-gate return(-1);
10980Sstevel@tonic-gate }
10990Sstevel@tonic-gate if (levels == level) {
11000Sstevel@tonic-gate astream += length;
11010Sstevel@tonic-gate }
11020Sstevel@tonic-gate }
11030Sstevel@tonic-gate return(-1);
11040Sstevel@tonic-gate }
11050Sstevel@tonic-gate
11060Sstevel@tonic-gate /*
11070Sstevel@tonic-gate * Routines that validate a TGS request; checks a lot of things. :-)
11080Sstevel@tonic-gate *
11090Sstevel@tonic-gate * Returns a Kerberos protocol error number, which is _not_ the same
11100Sstevel@tonic-gate * as a com_err error number!
11110Sstevel@tonic-gate */
11120Sstevel@tonic-gate #define TGS_OPTIONS_HANDLED (KDC_OPT_FORWARDABLE | KDC_OPT_FORWARDED | \
11130Sstevel@tonic-gate KDC_OPT_PROXIABLE | KDC_OPT_PROXY | \
11140Sstevel@tonic-gate KDC_OPT_ALLOW_POSTDATE | KDC_OPT_POSTDATED | \
11150Sstevel@tonic-gate KDC_OPT_RENEWABLE | KDC_OPT_RENEWABLE_OK | \
11160Sstevel@tonic-gate KDC_OPT_ENC_TKT_IN_SKEY | KDC_OPT_RENEW | \
11170Sstevel@tonic-gate KDC_OPT_VALIDATE)
11180Sstevel@tonic-gate
11190Sstevel@tonic-gate #define NO_TGT_OPTION (KDC_OPT_FORWARDED | KDC_OPT_PROXY | KDC_OPT_RENEW | \
11200Sstevel@tonic-gate KDC_OPT_VALIDATE)
11210Sstevel@tonic-gate
11220Sstevel@tonic-gate int
validate_tgs_request(register krb5_kdc_req * request,krb5_db_entry server,krb5_ticket * ticket,krb5_timestamp kdc_time,const char ** status)11232881Smp153739 validate_tgs_request(register krb5_kdc_req *request, krb5_db_entry server,
11242881Smp153739 krb5_ticket *ticket, krb5_timestamp kdc_time,
11252881Smp153739 const char **status)
11260Sstevel@tonic-gate {
11270Sstevel@tonic-gate int errcode;
11280Sstevel@tonic-gate int st_idx = 0;
11290Sstevel@tonic-gate
11300Sstevel@tonic-gate /*
11310Sstevel@tonic-gate * If an illegal option is set, ignore it.
11320Sstevel@tonic-gate */
11332881Smp153739 request->kdc_options &= TGS_OPTIONS_HANDLED;
11342881Smp153739
11350Sstevel@tonic-gate /* Check to see if server has expired */
11360Sstevel@tonic-gate if (server.expiration && server.expiration < kdc_time) {
11370Sstevel@tonic-gate *status = "SERVICE EXPIRED";
11380Sstevel@tonic-gate return(KDC_ERR_SERVICE_EXP);
11390Sstevel@tonic-gate }
11400Sstevel@tonic-gate
11410Sstevel@tonic-gate /*
11420Sstevel@tonic-gate * Verify that the server principal in authdat->ticket is correct
11430Sstevel@tonic-gate * (either the ticket granting service or the service that was
11440Sstevel@tonic-gate * originally requested)
11450Sstevel@tonic-gate */
11460Sstevel@tonic-gate if (request->kdc_options & NO_TGT_OPTION) {
11470Sstevel@tonic-gate if (!krb5_principal_compare(kdc_context, ticket->server, request->server)) {
11480Sstevel@tonic-gate *status = "SERVER DIDN'T MATCH TICKET FOR RENEW/FORWARD/ETC";
11490Sstevel@tonic-gate return(KDC_ERR_SERVER_NOMATCH);
11500Sstevel@tonic-gate }
11510Sstevel@tonic-gate } else {
11520Sstevel@tonic-gate /*
11530Sstevel@tonic-gate * OK, we need to validate the krbtgt service in the ticket.
11540Sstevel@tonic-gate *
11550Sstevel@tonic-gate * The krbtgt service is of the form:
11560Sstevel@tonic-gate * krbtgt/realm-A@realm-B
11570Sstevel@tonic-gate *
11580Sstevel@tonic-gate * Realm A is the "server realm"; the realm of the
11590Sstevel@tonic-gate * server of the requested ticket must match this realm.
11600Sstevel@tonic-gate * Of course, it should be a realm serviced by this KDC.
11610Sstevel@tonic-gate *
11620Sstevel@tonic-gate * Realm B is the "client realm"; this is what should be
11630Sstevel@tonic-gate * added to the transited field. (which is done elsewhere)
11640Sstevel@tonic-gate */
11650Sstevel@tonic-gate
11660Sstevel@tonic-gate /* Make sure there are two components... */
11670Sstevel@tonic-gate if (krb5_princ_size(kdc_context, ticket->server) != 2) {
11680Sstevel@tonic-gate *status = "BAD TGS SERVER LENGTH";
11690Sstevel@tonic-gate return KRB_AP_ERR_NOT_US;
11700Sstevel@tonic-gate }
11710Sstevel@tonic-gate /* ...that the first component is krbtgt... */
11720Sstevel@tonic-gate if (!krb5_is_tgs_principal(ticket->server)) {
11730Sstevel@tonic-gate *status = "BAD TGS SERVER NAME";
11740Sstevel@tonic-gate return KRB_AP_ERR_NOT_US;
11750Sstevel@tonic-gate }
11760Sstevel@tonic-gate /* ...and that the second component matches the server realm... */
11772881Smp153739 if ((krb5_princ_size(kdc_context, ticket->server) <= 1) ||
11782881Smp153739 (krb5_princ_component(kdc_context, ticket->server, 1)->length !=
11790Sstevel@tonic-gate krb5_princ_realm(kdc_context, request->server)->length) ||
11800Sstevel@tonic-gate memcmp(krb5_princ_component(kdc_context, ticket->server, 1)->data,
11810Sstevel@tonic-gate krb5_princ_realm(kdc_context, request->server)->data,
11820Sstevel@tonic-gate krb5_princ_realm(kdc_context, request->server)->length)) {
11830Sstevel@tonic-gate *status = "BAD TGS SERVER INSTANCE";
11840Sstevel@tonic-gate return KRB_AP_ERR_NOT_US;
11850Sstevel@tonic-gate }
11860Sstevel@tonic-gate /* XXX add check that second component must match locally
11870Sstevel@tonic-gate * supported realm?
11880Sstevel@tonic-gate */
11890Sstevel@tonic-gate
11900Sstevel@tonic-gate /* Server must allow TGS based issuances */
11910Sstevel@tonic-gate if (isflagset(server.attributes, KRB5_KDB_DISALLOW_TGT_BASED)) {
11920Sstevel@tonic-gate *status = "TGT BASED NOT ALLOWED";
11930Sstevel@tonic-gate return(KDC_ERR_POLICY);
11940Sstevel@tonic-gate }
11950Sstevel@tonic-gate }
11960Sstevel@tonic-gate
11970Sstevel@tonic-gate /* TGS must be forwardable to get forwarded or forwardable ticket */
11980Sstevel@tonic-gate if ((isflagset(request->kdc_options, KDC_OPT_FORWARDED) ||
11990Sstevel@tonic-gate isflagset(request->kdc_options, KDC_OPT_FORWARDABLE)) &&
12000Sstevel@tonic-gate !isflagset(ticket->enc_part2->flags, TKT_FLG_FORWARDABLE)) {
12010Sstevel@tonic-gate *status = "TGT NOT FORWARDABLE";
12020Sstevel@tonic-gate
12030Sstevel@tonic-gate return KDC_ERR_BADOPTION;
12040Sstevel@tonic-gate }
12050Sstevel@tonic-gate
12060Sstevel@tonic-gate /* TGS must be proxiable to get proxiable ticket */
12070Sstevel@tonic-gate if ((isflagset(request->kdc_options, KDC_OPT_PROXY) ||
12080Sstevel@tonic-gate isflagset(request->kdc_options, KDC_OPT_PROXIABLE)) &&
12090Sstevel@tonic-gate !isflagset(ticket->enc_part2->flags, TKT_FLG_PROXIABLE)) {
12100Sstevel@tonic-gate *status = "TGT NOT PROXIABLE";
12110Sstevel@tonic-gate return KDC_ERR_BADOPTION;
12120Sstevel@tonic-gate }
12130Sstevel@tonic-gate
12140Sstevel@tonic-gate /* TGS must allow postdating to get postdated ticket */
12150Sstevel@tonic-gate if ((isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE) ||
12160Sstevel@tonic-gate isflagset(request->kdc_options, KDC_OPT_POSTDATED)) &&
12170Sstevel@tonic-gate !isflagset(ticket->enc_part2->flags, TKT_FLG_MAY_POSTDATE)) {
12180Sstevel@tonic-gate *status = "TGT NOT POSTDATABLE";
12190Sstevel@tonic-gate return KDC_ERR_BADOPTION;
12200Sstevel@tonic-gate }
12210Sstevel@tonic-gate
12220Sstevel@tonic-gate /* can only validate invalid tix */
12230Sstevel@tonic-gate if (isflagset(request->kdc_options, KDC_OPT_VALIDATE) &&
12240Sstevel@tonic-gate !isflagset(ticket->enc_part2->flags, TKT_FLG_INVALID)) {
12250Sstevel@tonic-gate *status = "VALIDATE VALID TICKET";
12260Sstevel@tonic-gate return KDC_ERR_BADOPTION;
12270Sstevel@tonic-gate }
12280Sstevel@tonic-gate
12290Sstevel@tonic-gate /* can only renew renewable tix */
12300Sstevel@tonic-gate if ((isflagset(request->kdc_options, KDC_OPT_RENEW) ||
12310Sstevel@tonic-gate isflagset(request->kdc_options, KDC_OPT_RENEWABLE)) &&
12320Sstevel@tonic-gate !isflagset(ticket->enc_part2->flags, TKT_FLG_RENEWABLE)) {
12330Sstevel@tonic-gate *status = "TICKET NOT RENEWABLE";
12340Sstevel@tonic-gate return KDC_ERR_BADOPTION;
12350Sstevel@tonic-gate }
12360Sstevel@tonic-gate
12370Sstevel@tonic-gate /* can not proxy ticket granting tickets */
12380Sstevel@tonic-gate if (isflagset(request->kdc_options, KDC_OPT_PROXY) &&
12390Sstevel@tonic-gate (!request->server->data ||
12400Sstevel@tonic-gate request->server->data[0].length != KRB5_TGS_NAME_SIZE ||
12410Sstevel@tonic-gate memcmp(request->server->data[0].data, KRB5_TGS_NAME,
12420Sstevel@tonic-gate KRB5_TGS_NAME_SIZE))) {
12430Sstevel@tonic-gate *status = "CAN'T PROXY TGT";
12440Sstevel@tonic-gate return KDC_ERR_BADOPTION;
12450Sstevel@tonic-gate }
12460Sstevel@tonic-gate
12470Sstevel@tonic-gate /* Server must allow forwardable tickets */
12480Sstevel@tonic-gate if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE) &&
12490Sstevel@tonic-gate isflagset(server.attributes, KRB5_KDB_DISALLOW_FORWARDABLE)) {
12500Sstevel@tonic-gate *status = "NON-FORWARDABLE TICKET";
12510Sstevel@tonic-gate return(KDC_ERR_POLICY);
12520Sstevel@tonic-gate }
12530Sstevel@tonic-gate
12540Sstevel@tonic-gate /* Server must allow renewable tickets */
12550Sstevel@tonic-gate if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE) &&
12560Sstevel@tonic-gate isflagset(server.attributes, KRB5_KDB_DISALLOW_RENEWABLE)) {
12570Sstevel@tonic-gate *status = "NON-RENEWABLE TICKET";
12580Sstevel@tonic-gate return(KDC_ERR_POLICY);
12590Sstevel@tonic-gate }
12600Sstevel@tonic-gate
12610Sstevel@tonic-gate /* Server must allow proxiable tickets */
12620Sstevel@tonic-gate if (isflagset(request->kdc_options, KDC_OPT_PROXIABLE) &&
12630Sstevel@tonic-gate isflagset(server.attributes, KRB5_KDB_DISALLOW_PROXIABLE)) {
12640Sstevel@tonic-gate *status = "NON-PROXIABLE TICKET";
12650Sstevel@tonic-gate return(KDC_ERR_POLICY);
12660Sstevel@tonic-gate }
12670Sstevel@tonic-gate
12680Sstevel@tonic-gate /* Server must allow postdated tickets */
12690Sstevel@tonic-gate if (isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE) &&
12700Sstevel@tonic-gate isflagset(server.attributes, KRB5_KDB_DISALLOW_POSTDATED)) {
12710Sstevel@tonic-gate *status = "NON-POSTDATABLE TICKET";
12720Sstevel@tonic-gate return(KDC_ERR_CANNOT_POSTDATE);
12730Sstevel@tonic-gate }
12740Sstevel@tonic-gate
12750Sstevel@tonic-gate /* Server must allow DUP SKEY requests */
12760Sstevel@tonic-gate if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY) &&
12770Sstevel@tonic-gate isflagset(server.attributes, KRB5_KDB_DISALLOW_DUP_SKEY)) {
12780Sstevel@tonic-gate *status = "DUP_SKEY DISALLOWED";
12790Sstevel@tonic-gate return(KDC_ERR_POLICY);
12800Sstevel@tonic-gate }
12810Sstevel@tonic-gate
12820Sstevel@tonic-gate /* Server must not be locked out */
12830Sstevel@tonic-gate if (isflagset(server.attributes, KRB5_KDB_DISALLOW_ALL_TIX)) {
12840Sstevel@tonic-gate *status = "SERVER LOCKED OUT";
12850Sstevel@tonic-gate return(KDC_ERR_S_PRINCIPAL_UNKNOWN);
12860Sstevel@tonic-gate }
12870Sstevel@tonic-gate
12880Sstevel@tonic-gate /* Server must be allowed to be a service */
12890Sstevel@tonic-gate if (isflagset(server.attributes, KRB5_KDB_DISALLOW_SVR)) {
12900Sstevel@tonic-gate *status = "SERVER NOT ALLOWED";
12910Sstevel@tonic-gate return(KDC_ERR_S_PRINCIPAL_UNKNOWN);
12920Sstevel@tonic-gate }
12930Sstevel@tonic-gate
12940Sstevel@tonic-gate /* Check the hot list */
12950Sstevel@tonic-gate if (check_hot_list(ticket)) {
12960Sstevel@tonic-gate *status = "HOT_LIST";
12970Sstevel@tonic-gate return(KRB_AP_ERR_REPEAT);
12980Sstevel@tonic-gate }
12990Sstevel@tonic-gate
13000Sstevel@tonic-gate /* Check the start time vs. the KDC time */
13010Sstevel@tonic-gate if (isflagset(request->kdc_options, KDC_OPT_VALIDATE)) {
13020Sstevel@tonic-gate if (ticket->enc_part2->times.starttime > kdc_time) {
13030Sstevel@tonic-gate *status = "NOT_YET_VALID";
13040Sstevel@tonic-gate return(KRB_AP_ERR_TKT_NYV);
13050Sstevel@tonic-gate }
13060Sstevel@tonic-gate }
13070Sstevel@tonic-gate
13080Sstevel@tonic-gate /*
13090Sstevel@tonic-gate * Check the renew_till time. The endtime was already
13100Sstevel@tonic-gate * been checked in the initial authentication check.
13110Sstevel@tonic-gate */
13120Sstevel@tonic-gate if (isflagset(request->kdc_options, KDC_OPT_RENEW) &&
13130Sstevel@tonic-gate (ticket->enc_part2->times.renew_till < kdc_time)) {
13140Sstevel@tonic-gate *status = "TKT_EXPIRED";
13150Sstevel@tonic-gate return(KRB_AP_ERR_TKT_EXPIRED);
13160Sstevel@tonic-gate }
13170Sstevel@tonic-gate
13180Sstevel@tonic-gate /*
13190Sstevel@tonic-gate * Checks for ENC_TKT_IN_SKEY:
13200Sstevel@tonic-gate *
13210Sstevel@tonic-gate * (1) Make sure the second ticket exists
13220Sstevel@tonic-gate * (2) Make sure it is a ticket granting ticket
13230Sstevel@tonic-gate */
13240Sstevel@tonic-gate if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY)) {
13250Sstevel@tonic-gate if (!request->second_ticket ||
13260Sstevel@tonic-gate !request->second_ticket[st_idx]) {
13270Sstevel@tonic-gate *status = "NO_2ND_TKT";
13280Sstevel@tonic-gate return(KDC_ERR_BADOPTION);
13290Sstevel@tonic-gate }
13300Sstevel@tonic-gate if (!krb5_principal_compare(kdc_context, request->second_ticket[st_idx]->server,
13310Sstevel@tonic-gate tgs_server)) {
13320Sstevel@tonic-gate *status = "2ND_TKT_NOT_TGS";
13330Sstevel@tonic-gate return(KDC_ERR_POLICY);
13340Sstevel@tonic-gate }
13350Sstevel@tonic-gate st_idx++;
13360Sstevel@tonic-gate }
13370Sstevel@tonic-gate
13380Sstevel@tonic-gate /* Check for hardware preauthentication */
13390Sstevel@tonic-gate if (isflagset(server.attributes, KRB5_KDB_REQUIRES_HW_AUTH) &&
13400Sstevel@tonic-gate !isflagset(ticket->enc_part2->flags,TKT_FLG_HW_AUTH)) {
13410Sstevel@tonic-gate *status = "NO HW PREAUTH";
13420Sstevel@tonic-gate return KRB_ERR_GENERIC;
13430Sstevel@tonic-gate }
13440Sstevel@tonic-gate
13450Sstevel@tonic-gate /* Check for any kind of preauthentication */
13460Sstevel@tonic-gate if (isflagset(server.attributes, KRB5_KDB_REQUIRES_PRE_AUTH) &&
13470Sstevel@tonic-gate !isflagset(ticket->enc_part2->flags, TKT_FLG_PRE_AUTH)) {
13480Sstevel@tonic-gate *status = "NO PREAUTH";
13490Sstevel@tonic-gate return KRB_ERR_GENERIC;
13500Sstevel@tonic-gate }
13510Sstevel@tonic-gate
13520Sstevel@tonic-gate /*
13530Sstevel@tonic-gate * Check local policy
13540Sstevel@tonic-gate */
13550Sstevel@tonic-gate errcode = against_local_policy_tgs(request, server, ticket, status);
13560Sstevel@tonic-gate if (errcode)
13570Sstevel@tonic-gate return errcode;
13580Sstevel@tonic-gate
13590Sstevel@tonic-gate
13600Sstevel@tonic-gate return 0;
13610Sstevel@tonic-gate }
13620Sstevel@tonic-gate
13630Sstevel@tonic-gate /*
13640Sstevel@tonic-gate * This function returns 1 if the dbentry has a key for a specified
13650Sstevel@tonic-gate * keytype, and 0 if not.
13660Sstevel@tonic-gate */
13670Sstevel@tonic-gate int
dbentry_has_key_for_enctype(krb5_context context,krb5_db_entry * client,krb5_enctype enctype)13682881Smp153739 dbentry_has_key_for_enctype(krb5_context context, krb5_db_entry *client,
13692881Smp153739 krb5_enctype enctype)
13700Sstevel@tonic-gate {
13710Sstevel@tonic-gate krb5_error_code retval;
13720Sstevel@tonic-gate krb5_key_data *datap;
13730Sstevel@tonic-gate
13740Sstevel@tonic-gate retval = krb5_dbe_find_enctype(context, client, enctype,
13750Sstevel@tonic-gate -1, 0, &datap);
13760Sstevel@tonic-gate if (retval)
13770Sstevel@tonic-gate return 0;
13780Sstevel@tonic-gate else
13790Sstevel@tonic-gate return 1;
13800Sstevel@tonic-gate }
13810Sstevel@tonic-gate
13820Sstevel@tonic-gate /*
13830Sstevel@tonic-gate * This function returns 1 if the entity referenced by this
13840Sstevel@tonic-gate * structure can support the a particular encryption system, and 0 if
13850Sstevel@tonic-gate * not.
13860Sstevel@tonic-gate *
13870Sstevel@tonic-gate * XXX eventually this information should be looked up in the
13880Sstevel@tonic-gate * database. Since it isn't, we use some hueristics and attribute
13890Sstevel@tonic-gate * options bits for now.
13900Sstevel@tonic-gate */
13910Sstevel@tonic-gate int
dbentry_supports_enctype(krb5_context context,krb5_db_entry * client,krb5_enctype enctype)13922881Smp153739 dbentry_supports_enctype(krb5_context context, krb5_db_entry *client,
13932881Smp153739 krb5_enctype enctype)
13940Sstevel@tonic-gate {
13950Sstevel@tonic-gate /*
13960Sstevel@tonic-gate * If it's DES_CBC_MD5, there's a bit in the attribute mask which
13970Sstevel@tonic-gate * checks to see if we support it.
13980Sstevel@tonic-gate *
13990Sstevel@tonic-gate * In theory everything's supposed to support DES_CBC_MD5, but
14000Sstevel@tonic-gate * that's not the reality....
14010Sstevel@tonic-gate */
14020Sstevel@tonic-gate
14030Sstevel@tonic-gate /*
14040Sstevel@tonic-gate * We are assuming that all entries can support MD5; this information
14050Sstevel@tonic-gate * need not be kept in the database.
14060Sstevel@tonic-gate */
14070Sstevel@tonic-gate
14080Sstevel@tonic-gate
14090Sstevel@tonic-gate if (enctype == ENCTYPE_DES_CBC_MD5)
14100Sstevel@tonic-gate return 1;
14110Sstevel@tonic-gate
14120Sstevel@tonic-gate /*
14130Sstevel@tonic-gate * XXX we assume everything can understand DES_CBC_CRC
14140Sstevel@tonic-gate */
14150Sstevel@tonic-gate if (enctype == ENCTYPE_DES_CBC_CRC)
14160Sstevel@tonic-gate return 1;
14170Sstevel@tonic-gate
14180Sstevel@tonic-gate /*
14190Sstevel@tonic-gate * If we have a key for the encryption system, we assume it's
14200Sstevel@tonic-gate * supported.
14210Sstevel@tonic-gate */
14220Sstevel@tonic-gate return dbentry_has_key_for_enctype(context, client, enctype);
14230Sstevel@tonic-gate }
14240Sstevel@tonic-gate
14250Sstevel@tonic-gate /*
14260Sstevel@tonic-gate * This function returns the keytype which should be selected for the
14270Sstevel@tonic-gate * session key. It is based on the ordered list which the user
14280Sstevel@tonic-gate * requested, and what the KDC and the application server can support.
14290Sstevel@tonic-gate */
14300Sstevel@tonic-gate krb5_enctype
select_session_keytype(krb5_context context,krb5_db_entry * server,int nktypes,krb5_enctype * ktype)14312881Smp153739 select_session_keytype(krb5_context context, krb5_db_entry *server,
14322881Smp153739 int nktypes, krb5_enctype *ktype)
14330Sstevel@tonic-gate {
14340Sstevel@tonic-gate int i;
14350Sstevel@tonic-gate
14360Sstevel@tonic-gate for (i = 0; i < nktypes; i++) {
1437781Sgtb if (!krb5_c_valid_enctype(ktype[i]))
14380Sstevel@tonic-gate continue;
14390Sstevel@tonic-gate
14402881Smp153739 if (!krb5_is_permitted_enctype(context, ktype[i]))
14412881Smp153739 continue;
14422881Smp153739
14430Sstevel@tonic-gate if (dbentry_supports_enctype(context, server, ktype[i]))
14440Sstevel@tonic-gate return ktype[i];
14450Sstevel@tonic-gate }
14460Sstevel@tonic-gate return 0;
14470Sstevel@tonic-gate }
14480Sstevel@tonic-gate
14490Sstevel@tonic-gate /*
14500Sstevel@tonic-gate * This function returns salt information for a particular client_key
14510Sstevel@tonic-gate */
14520Sstevel@tonic-gate krb5_error_code
get_salt_from_key(krb5_context context,krb5_principal client,krb5_key_data * client_key,krb5_data * salt)14532881Smp153739 get_salt_from_key(krb5_context context, krb5_principal client,
14542881Smp153739 krb5_key_data *client_key, krb5_data *salt)
14550Sstevel@tonic-gate {
14560Sstevel@tonic-gate krb5_error_code retval;
14570Sstevel@tonic-gate krb5_data * realm;
14580Sstevel@tonic-gate
14590Sstevel@tonic-gate salt->data = 0;
14602881Smp153739 salt->length = SALT_TYPE_NO_LENGTH;
14610Sstevel@tonic-gate
14620Sstevel@tonic-gate if (client_key->key_data_ver == 1)
14630Sstevel@tonic-gate return 0;
14640Sstevel@tonic-gate
14650Sstevel@tonic-gate switch (client_key->key_data_type[1]) {
14660Sstevel@tonic-gate case KRB5_KDB_SALTTYPE_NORMAL:
14670Sstevel@tonic-gate break;
14680Sstevel@tonic-gate case KRB5_KDB_SALTTYPE_V4:
14690Sstevel@tonic-gate /* send an empty (V4) salt */
14700Sstevel@tonic-gate salt->data = 0;
14710Sstevel@tonic-gate salt->length = 0;
14720Sstevel@tonic-gate break;
14730Sstevel@tonic-gate case KRB5_KDB_SALTTYPE_NOREALM:
14740Sstevel@tonic-gate if ((retval = krb5_principal2salt_norealm(context, client, salt)))
14750Sstevel@tonic-gate return retval;
14760Sstevel@tonic-gate break;
14770Sstevel@tonic-gate case KRB5_KDB_SALTTYPE_AFS3:
14780Sstevel@tonic-gate /* send the same salt as with onlyrealm - but with no type info,
14790Sstevel@tonic-gate we just hope they figure it out on the other end. */
14800Sstevel@tonic-gate /* fall through to onlyrealm: */
14810Sstevel@tonic-gate case KRB5_KDB_SALTTYPE_ONLYREALM:
14820Sstevel@tonic-gate realm = krb5_princ_realm(context, client);
14830Sstevel@tonic-gate salt->length = realm->length;
14840Sstevel@tonic-gate if ((salt->data = malloc(realm->length)) == NULL)
14850Sstevel@tonic-gate return ENOMEM;
14860Sstevel@tonic-gate memcpy(salt->data, realm->data, realm->length);
14870Sstevel@tonic-gate break;
14880Sstevel@tonic-gate case KRB5_KDB_SALTTYPE_SPECIAL:
14890Sstevel@tonic-gate salt->length = client_key->key_data_length[1];
14900Sstevel@tonic-gate if ((salt->data = malloc(salt->length)) == NULL)
14910Sstevel@tonic-gate return ENOMEM;
14920Sstevel@tonic-gate memcpy(salt->data, client_key->key_data_contents[1], salt->length);
14930Sstevel@tonic-gate break;
14940Sstevel@tonic-gate }
14950Sstevel@tonic-gate return 0;
14960Sstevel@tonic-gate }
14970Sstevel@tonic-gate
14980Sstevel@tonic-gate /*
14990Sstevel@tonic-gate * Limit strings to a "reasonable" length to prevent crowding out of
15000Sstevel@tonic-gate * other useful information in the log entry
15010Sstevel@tonic-gate */
15020Sstevel@tonic-gate #define NAME_LENGTH_LIMIT 128
15030Sstevel@tonic-gate
limit_string(char * name)15040Sstevel@tonic-gate void limit_string(char *name)
15050Sstevel@tonic-gate {
15060Sstevel@tonic-gate int i;
15070Sstevel@tonic-gate
15080Sstevel@tonic-gate if (!name)
15090Sstevel@tonic-gate return;
15100Sstevel@tonic-gate
15110Sstevel@tonic-gate if (strlen(name) < NAME_LENGTH_LIMIT)
15120Sstevel@tonic-gate return;
15130Sstevel@tonic-gate
15140Sstevel@tonic-gate i = NAME_LENGTH_LIMIT-4;
15150Sstevel@tonic-gate name[i++] = '.';
15160Sstevel@tonic-gate name[i++] = '.';
15170Sstevel@tonic-gate name[i++] = '.';
15180Sstevel@tonic-gate name[i] = '\0';
15190Sstevel@tonic-gate return;
15200Sstevel@tonic-gate }
15212881Smp153739
15222881Smp153739 /*
15232881Smp153739 * L10_2 = log10(2**x), rounded up; log10(2) ~= 0.301.
15242881Smp153739 */
15252881Smp153739 #define L10_2(x) ((int)(((x * 301) + 999) / 1000))
15262881Smp153739
15272881Smp153739 /*
15282881Smp153739 * Max length of sprintf("%ld") for an int of type T; includes leading
15292881Smp153739 * minus sign and terminating NUL.
15302881Smp153739 */
15312881Smp153739 #define D_LEN(t) (L10_2(sizeof(t) * CHAR_BIT) + 2)
15322881Smp153739
15332881Smp153739 void
ktypes2str(char * s,size_t len,int nktypes,krb5_enctype * ktype)15342881Smp153739 ktypes2str(char *s, size_t len, int nktypes, krb5_enctype *ktype)
15352881Smp153739 {
15362881Smp153739 int i;
15372881Smp153739 char stmp[D_LEN(krb5_enctype) + 1];
15382881Smp153739 char *p;
15392881Smp153739
15402881Smp153739 if (nktypes < 0
15412881Smp153739 || len < (sizeof(" etypes {...}") + D_LEN(int))) {
15422881Smp153739 *s = '\0';
15432881Smp153739 return;
15442881Smp153739 }
15452881Smp153739
15462881Smp153739 sprintf(s, "%d etypes {", nktypes);
15472881Smp153739 for (i = 0; i < nktypes; i++) {
15482881Smp153739 sprintf(stmp, "%s%ld", i ? " " : "", (long)ktype[i]);
15492881Smp153739 if (strlen(s) + strlen(stmp) + sizeof("}") > len)
15502881Smp153739 break;
15512881Smp153739 strcat(s, stmp);
15522881Smp153739 }
15532881Smp153739 if (i < nktypes) {
15542881Smp153739 /*
15552881Smp153739 * We broke out of the loop. Try to truncate the list.
15562881Smp153739 */
15572881Smp153739 p = s + strlen(s);
15582881Smp153739 while (p - s + sizeof("...}") > len) {
15592881Smp153739 while (p > s && *p != ' ' && *p != '{')
15602881Smp153739 *p-- = '\0';
15612881Smp153739 if (p > s && *p == ' ') {
15622881Smp153739 *p-- = '\0';
15632881Smp153739 continue;
15642881Smp153739 }
15652881Smp153739 }
15662881Smp153739 strcat(s, "...");
15672881Smp153739 }
15682881Smp153739 strcat(s, "}");
15692881Smp153739 return;
15702881Smp153739 }
15712881Smp153739
15722881Smp153739 void
rep_etypes2str(char * s,size_t len,krb5_kdc_rep * rep)15732881Smp153739 rep_etypes2str(char *s, size_t len, krb5_kdc_rep *rep)
15742881Smp153739 {
15752881Smp153739 char stmp[sizeof("ses=") + D_LEN(krb5_enctype)];
15762881Smp153739
15772881Smp153739 if (len < (3 * D_LEN(krb5_enctype)
15782881Smp153739 + sizeof("etypes {rep= tkt= ses=}"))) {
15792881Smp153739 *s = '\0';
15802881Smp153739 return;
15812881Smp153739 }
15822881Smp153739
15832881Smp153739 sprintf(s, "etypes {rep=%ld", (long)rep->enc_part.enctype);
15842881Smp153739
15852881Smp153739 if (rep->ticket != NULL) {
15862881Smp153739 sprintf(stmp, " tkt=%ld", (long)rep->ticket->enc_part.enctype);
15872881Smp153739 strcat(s, stmp);
15882881Smp153739 }
15892881Smp153739
15902881Smp153739 if (rep->ticket != NULL
15912881Smp153739 && rep->ticket->enc_part2 != NULL
15922881Smp153739 && rep->ticket->enc_part2->session != NULL) {
15932881Smp153739 sprintf(stmp, " ses=%ld",
15942881Smp153739 (long)rep->ticket->enc_part2->session->enctype);
15952881Smp153739 strcat(s, stmp);
15962881Smp153739 }
15972881Smp153739 strcat(s, "}");
15982881Smp153739 return;
15992881Smp153739 }
1600