17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * kdc/kdc_util.c 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * Copyright 1990,1991 by the Massachusetts Institute of Technology. 57c478bd9Sstevel@tonic-gate * All Rights Reserved. 67c478bd9Sstevel@tonic-gate * 77c478bd9Sstevel@tonic-gate * Export of this software from the United States of America may 87c478bd9Sstevel@tonic-gate * require a specific license from the United States Government. 97c478bd9Sstevel@tonic-gate * It is the responsibility of any person or organization contemplating 107c478bd9Sstevel@tonic-gate * export to obtain such a license before exporting. 117c478bd9Sstevel@tonic-gate * 127c478bd9Sstevel@tonic-gate * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 137c478bd9Sstevel@tonic-gate * distribute this software and its documentation for any purpose and 147c478bd9Sstevel@tonic-gate * without fee is hereby granted, provided that the above copyright 157c478bd9Sstevel@tonic-gate * notice appear in all copies and that both that copyright notice and 167c478bd9Sstevel@tonic-gate * this permission notice appear in supporting documentation, and that 177c478bd9Sstevel@tonic-gate * the name of M.I.T. not be used in advertising or publicity pertaining 187c478bd9Sstevel@tonic-gate * to distribution of the software without specific, written prior 197c478bd9Sstevel@tonic-gate * permission. Furthermore if you modify this software you must label 207c478bd9Sstevel@tonic-gate * your software as modified software and not distribute it in such a 217c478bd9Sstevel@tonic-gate * fashion that it might be confused with the original M.I.T. software. 227c478bd9Sstevel@tonic-gate * M.I.T. makes no representations about the suitability of 237c478bd9Sstevel@tonic-gate * this software for any purpose. It is provided "as is" without express 247c478bd9Sstevel@tonic-gate * or implied warranty. 257c478bd9Sstevel@tonic-gate * 267c478bd9Sstevel@tonic-gate * 277c478bd9Sstevel@tonic-gate * Utility functions for the KDC implementation. 287c478bd9Sstevel@tonic-gate */ 297c478bd9Sstevel@tonic-gate 307c478bd9Sstevel@tonic-gate 317c478bd9Sstevel@tonic-gate 327c478bd9Sstevel@tonic-gate #include "k5-int.h" 337c478bd9Sstevel@tonic-gate #include "kdc_util.h" 347c478bd9Sstevel@tonic-gate #include "extern.h" 357c478bd9Sstevel@tonic-gate #include <stdio.h> 3656a424ccSmp153739 #include <ctype.h> 377c478bd9Sstevel@tonic-gate #include <syslog.h> 387c478bd9Sstevel@tonic-gate #include "adm.h" 397c478bd9Sstevel@tonic-gate #include "adm_proto.h" 4056a424ccSmp153739 #include <limits.h> 417c478bd9Sstevel@tonic-gate 427c478bd9Sstevel@tonic-gate #ifdef USE_RCACHE 437c478bd9Sstevel@tonic-gate static char *kdc_current_rcname = (char *) NULL; 447c478bd9Sstevel@tonic-gate krb5_deltat rc_lifetime; /* See kdc_initialize_rcache() */ 457c478bd9Sstevel@tonic-gate #endif 467c478bd9Sstevel@tonic-gate 477c478bd9Sstevel@tonic-gate #ifdef USE_RCACHE 487c478bd9Sstevel@tonic-gate /* 497c478bd9Sstevel@tonic-gate * initialize the replay cache. 507c478bd9Sstevel@tonic-gate */ 517c478bd9Sstevel@tonic-gate krb5_error_code 5256a424ccSmp153739 kdc_initialize_rcache(krb5_context kcontext, char *rcache_name) 537c478bd9Sstevel@tonic-gate { 547c478bd9Sstevel@tonic-gate krb5_error_code retval; 557c478bd9Sstevel@tonic-gate char *rcname; 567c478bd9Sstevel@tonic-gate char *sname; 577c478bd9Sstevel@tonic-gate 587c478bd9Sstevel@tonic-gate rcname = (rcache_name) ? rcache_name : kdc_current_rcname; 597c478bd9Sstevel@tonic-gate 607c478bd9Sstevel@tonic-gate /* rc_lifetime used elsewhere to verify we're not */ 617c478bd9Sstevel@tonic-gate /* replaying really old data */ 627c478bd9Sstevel@tonic-gate rc_lifetime = kcontext->clockskew; 637c478bd9Sstevel@tonic-gate 647c478bd9Sstevel@tonic-gate if (!rcname) 657c478bd9Sstevel@tonic-gate rcname = KDCRCACHE; 667c478bd9Sstevel@tonic-gate if (!(retval = krb5_rc_resolve_full(kcontext, &kdc_rcache, rcname))) { 677c478bd9Sstevel@tonic-gate /* Recover or initialize the replay cache */ 687c478bd9Sstevel@tonic-gate if (!(retval = krb5_rc_recover(kcontext, kdc_rcache)) || 697c478bd9Sstevel@tonic-gate !(retval = krb5_rc_initialize(kcontext, 707c478bd9Sstevel@tonic-gate kdc_rcache, 717c478bd9Sstevel@tonic-gate kcontext->clockskew)) 727c478bd9Sstevel@tonic-gate ) { 737c478bd9Sstevel@tonic-gate /* Expunge the replay cache */ 747c478bd9Sstevel@tonic-gate if (!(retval = krb5_rc_expunge(kcontext, kdc_rcache))) { 757c478bd9Sstevel@tonic-gate sname = kdc_current_rcname; 767c478bd9Sstevel@tonic-gate kdc_current_rcname = strdup(rcname); 777c478bd9Sstevel@tonic-gate if (sname) 787c478bd9Sstevel@tonic-gate free(sname); 797c478bd9Sstevel@tonic-gate } 807c478bd9Sstevel@tonic-gate } 817c478bd9Sstevel@tonic-gate if (retval) 827c478bd9Sstevel@tonic-gate krb5_rc_close(kcontext, kdc_rcache); 837c478bd9Sstevel@tonic-gate } 847c478bd9Sstevel@tonic-gate return(retval); 857c478bd9Sstevel@tonic-gate } 867c478bd9Sstevel@tonic-gate #endif 877c478bd9Sstevel@tonic-gate 887c478bd9Sstevel@tonic-gate /* 897c478bd9Sstevel@tonic-gate * concatenate first two authdata arrays, returning an allocated replacement. 907c478bd9Sstevel@tonic-gate * The replacement should be freed with krb5_free_authdata(). 917c478bd9Sstevel@tonic-gate */ 927c478bd9Sstevel@tonic-gate krb5_error_code 9356a424ccSmp153739 concat_authorization_data(krb5_authdata **first, krb5_authdata **second, 9456a424ccSmp153739 krb5_authdata ***output) 957c478bd9Sstevel@tonic-gate { 967c478bd9Sstevel@tonic-gate register int i, j; 977c478bd9Sstevel@tonic-gate register krb5_authdata **ptr, **retdata; 987c478bd9Sstevel@tonic-gate 997c478bd9Sstevel@tonic-gate /* count up the entries */ 1007c478bd9Sstevel@tonic-gate i = 0; 1017c478bd9Sstevel@tonic-gate if (first) 1027c478bd9Sstevel@tonic-gate for (ptr = first; *ptr; ptr++) 1037c478bd9Sstevel@tonic-gate i++; 1047c478bd9Sstevel@tonic-gate if (second) 1057c478bd9Sstevel@tonic-gate for (ptr = second; *ptr; ptr++) 1067c478bd9Sstevel@tonic-gate i++; 1077c478bd9Sstevel@tonic-gate 1087c478bd9Sstevel@tonic-gate retdata = (krb5_authdata **)malloc((i+1)*sizeof(*retdata)); 1097c478bd9Sstevel@tonic-gate if (!retdata) 1107c478bd9Sstevel@tonic-gate return ENOMEM; 1117c478bd9Sstevel@tonic-gate retdata[i] = 0; /* null-terminated array */ 1127c478bd9Sstevel@tonic-gate for (i = 0, j = 0, ptr = first; j < 2 ; ptr = second, j++) 1137c478bd9Sstevel@tonic-gate while (ptr && *ptr) { 1147c478bd9Sstevel@tonic-gate /* now walk & copy */ 1157c478bd9Sstevel@tonic-gate retdata[i] = (krb5_authdata *)malloc(sizeof(*retdata[i])); 1167c478bd9Sstevel@tonic-gate if (!retdata[i]) { 1177c478bd9Sstevel@tonic-gate krb5_free_authdata(kdc_context, retdata); 1187c478bd9Sstevel@tonic-gate return ENOMEM; 1197c478bd9Sstevel@tonic-gate } 1207c478bd9Sstevel@tonic-gate *retdata[i] = **ptr; 1217c478bd9Sstevel@tonic-gate if (!(retdata[i]->contents = 1227c478bd9Sstevel@tonic-gate (krb5_octet *)malloc(retdata[i]->length))) { 1237c478bd9Sstevel@tonic-gate free((char *)retdata[i]); 1247c478bd9Sstevel@tonic-gate retdata[i] = 0; 1257c478bd9Sstevel@tonic-gate krb5_free_authdata(kdc_context, retdata); 1267c478bd9Sstevel@tonic-gate return ENOMEM; 1277c478bd9Sstevel@tonic-gate } 1287c478bd9Sstevel@tonic-gate memcpy((char *) retdata[i]->contents, 1297c478bd9Sstevel@tonic-gate (char *)(*ptr)->contents, 1307c478bd9Sstevel@tonic-gate retdata[i]->length); 1317c478bd9Sstevel@tonic-gate 1327c478bd9Sstevel@tonic-gate ptr++; 1337c478bd9Sstevel@tonic-gate i++; 1347c478bd9Sstevel@tonic-gate } 1357c478bd9Sstevel@tonic-gate *output = retdata; 1367c478bd9Sstevel@tonic-gate return 0; 1377c478bd9Sstevel@tonic-gate } 1387c478bd9Sstevel@tonic-gate 1397c478bd9Sstevel@tonic-gate krb5_boolean 14056a424ccSmp153739 realm_compare(krb5_principal princ1, krb5_principal princ2) 1417c478bd9Sstevel@tonic-gate { 1427c478bd9Sstevel@tonic-gate krb5_data *realm1 = krb5_princ_realm(kdc_context, princ1); 1437c478bd9Sstevel@tonic-gate krb5_data *realm2 = krb5_princ_realm(kdc_context, princ2); 1447c478bd9Sstevel@tonic-gate 1457c478bd9Sstevel@tonic-gate return((realm1->length == realm2->length) && 1467c478bd9Sstevel@tonic-gate !memcmp(realm1->data, realm2->data, realm1->length)); 1477c478bd9Sstevel@tonic-gate } 1487c478bd9Sstevel@tonic-gate 1497c478bd9Sstevel@tonic-gate /* 1507c478bd9Sstevel@tonic-gate * Returns TRUE if the kerberos principal is the name of a Kerberos ticket 1517c478bd9Sstevel@tonic-gate * service. 1527c478bd9Sstevel@tonic-gate */ 15356a424ccSmp153739 krb5_boolean krb5_is_tgs_principal(krb5_principal principal) 1547c478bd9Sstevel@tonic-gate { 15556a424ccSmp153739 if ((krb5_princ_size(kdc_context, principal) > 0) && 1567c478bd9Sstevel@tonic-gate (krb5_princ_component(kdc_context, principal, 0)->length == 1577c478bd9Sstevel@tonic-gate KRB5_TGS_NAME_SIZE) && 1587c478bd9Sstevel@tonic-gate (!memcmp(krb5_princ_component(kdc_context, principal, 0)->data, 1597c478bd9Sstevel@tonic-gate KRB5_TGS_NAME, KRB5_TGS_NAME_SIZE))) 1607c478bd9Sstevel@tonic-gate return TRUE; 1617c478bd9Sstevel@tonic-gate return FALSE; 1627c478bd9Sstevel@tonic-gate } 1637c478bd9Sstevel@tonic-gate 1647c478bd9Sstevel@tonic-gate /* 1657c478bd9Sstevel@tonic-gate * given authentication data (provides seed for checksum), verify checksum 1667c478bd9Sstevel@tonic-gate * for source data. 1677c478bd9Sstevel@tonic-gate */ 1687c478bd9Sstevel@tonic-gate static krb5_error_code 16956a424ccSmp153739 comp_cksum(krb5_context kcontext, krb5_data *source, krb5_ticket *ticket, 17056a424ccSmp153739 krb5_checksum *his_cksum) 1717c478bd9Sstevel@tonic-gate { 1727c478bd9Sstevel@tonic-gate krb5_error_code retval; 1737c478bd9Sstevel@tonic-gate krb5_boolean valid; 1747c478bd9Sstevel@tonic-gate 175505d05c7Sgtb if (!krb5_c_valid_cksumtype(his_cksum->checksum_type)) 1767c478bd9Sstevel@tonic-gate return KRB5KDC_ERR_SUMTYPE_NOSUPP; 1777c478bd9Sstevel@tonic-gate 1787c478bd9Sstevel@tonic-gate /* must be collision proof */ 179505d05c7Sgtb if (!krb5_c_is_coll_proof_cksum(his_cksum->checksum_type)) 1807c478bd9Sstevel@tonic-gate return KRB5KRB_AP_ERR_INAPP_CKSUM; 1817c478bd9Sstevel@tonic-gate 1827c478bd9Sstevel@tonic-gate /* verify checksum */ 1837c478bd9Sstevel@tonic-gate if ((retval = krb5_c_verify_checksum(kcontext, ticket->enc_part2->session, 1847c478bd9Sstevel@tonic-gate KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM, 1857c478bd9Sstevel@tonic-gate source, his_cksum, &valid))) 1867c478bd9Sstevel@tonic-gate return(retval); 1877c478bd9Sstevel@tonic-gate 1887c478bd9Sstevel@tonic-gate if (!valid) 1897c478bd9Sstevel@tonic-gate return(KRB5KRB_AP_ERR_BAD_INTEGRITY); 1907c478bd9Sstevel@tonic-gate 1917c478bd9Sstevel@tonic-gate return(0); 1927c478bd9Sstevel@tonic-gate } 1937c478bd9Sstevel@tonic-gate 1947c478bd9Sstevel@tonic-gate krb5_error_code 19556a424ccSmp153739 kdc_process_tgs_req(krb5_kdc_req *request, const krb5_fulladdr *from, 19656a424ccSmp153739 krb5_data *pkt, krb5_ticket **ticket, 19756a424ccSmp153739 krb5_keyblock **subkey) 1987c478bd9Sstevel@tonic-gate { 1997c478bd9Sstevel@tonic-gate krb5_pa_data ** tmppa; 2007c478bd9Sstevel@tonic-gate krb5_ap_req * apreq; 2017c478bd9Sstevel@tonic-gate krb5_error_code retval; 2027c478bd9Sstevel@tonic-gate krb5_data scratch1; 2037c478bd9Sstevel@tonic-gate krb5_data * scratch = NULL; 2047c478bd9Sstevel@tonic-gate krb5_boolean foreign_server = FALSE; 2057c478bd9Sstevel@tonic-gate krb5_auth_context auth_context = NULL; 2067c478bd9Sstevel@tonic-gate krb5_authenticator * authenticator = NULL; 2077c478bd9Sstevel@tonic-gate krb5_checksum * his_cksum = NULL; 20856a424ccSmp153739 /* krb5_keyblock * key = NULL;*/ 20956a424ccSmp153739 /* krb5_kvno kvno = 0;*/ 2107c478bd9Sstevel@tonic-gate 2117c478bd9Sstevel@tonic-gate if (!request->padata) 2127c478bd9Sstevel@tonic-gate return KRB5KDC_ERR_PADATA_TYPE_NOSUPP; 2137c478bd9Sstevel@tonic-gate for (tmppa = request->padata; *tmppa; tmppa++) { 2147c478bd9Sstevel@tonic-gate if ((*tmppa)->pa_type == KRB5_PADATA_AP_REQ) 2157c478bd9Sstevel@tonic-gate break; 2167c478bd9Sstevel@tonic-gate } 2177c478bd9Sstevel@tonic-gate if (!*tmppa) /* cannot find any AP_REQ */ 2187c478bd9Sstevel@tonic-gate return KRB5KDC_ERR_PADATA_TYPE_NOSUPP; 2197c478bd9Sstevel@tonic-gate 2207c478bd9Sstevel@tonic-gate scratch1.length = (*tmppa)->length; 2217c478bd9Sstevel@tonic-gate scratch1.data = (char *)(*tmppa)->contents; 2227c478bd9Sstevel@tonic-gate if ((retval = decode_krb5_ap_req(&scratch1, &apreq))) 2237c478bd9Sstevel@tonic-gate return retval; 2247c478bd9Sstevel@tonic-gate 2257c478bd9Sstevel@tonic-gate if (isflagset(apreq->ap_options, AP_OPTS_USE_SESSION_KEY) || 2267c478bd9Sstevel@tonic-gate isflagset(apreq->ap_options, AP_OPTS_MUTUAL_REQUIRED)) { 2277c478bd9Sstevel@tonic-gate krb5_klog_syslog(LOG_INFO, "TGS_REQ: SESSION KEY or MUTUAL"); 2287c478bd9Sstevel@tonic-gate retval = KRB5KDC_ERR_POLICY; 2297c478bd9Sstevel@tonic-gate goto cleanup; 2307c478bd9Sstevel@tonic-gate } 2317c478bd9Sstevel@tonic-gate 2327c478bd9Sstevel@tonic-gate /* If the "server" principal in the ticket is not something 2337c478bd9Sstevel@tonic-gate in the local realm, then we must refuse to service the request 2347c478bd9Sstevel@tonic-gate if the client claims to be from the local realm. 2357c478bd9Sstevel@tonic-gate 2367c478bd9Sstevel@tonic-gate If we don't do this, then some other realm's nasty KDC can 2377c478bd9Sstevel@tonic-gate claim to be authenticating a client from our realm, and we'll 2387c478bd9Sstevel@tonic-gate give out tickets concurring with it! 2397c478bd9Sstevel@tonic-gate 2407c478bd9Sstevel@tonic-gate we set a flag here for checking below. 2417c478bd9Sstevel@tonic-gate */ 2427c478bd9Sstevel@tonic-gate if ((krb5_princ_realm(kdc_context, apreq->ticket->server)->length != 2437c478bd9Sstevel@tonic-gate krb5_princ_realm(kdc_context, tgs_server)->length) || 2447c478bd9Sstevel@tonic-gate memcmp(krb5_princ_realm(kdc_context, apreq->ticket->server)->data, 2457c478bd9Sstevel@tonic-gate krb5_princ_realm(kdc_context, tgs_server)->data, 2467c478bd9Sstevel@tonic-gate krb5_princ_realm(kdc_context, tgs_server)->length)) 2477c478bd9Sstevel@tonic-gate foreign_server = TRUE; 2487c478bd9Sstevel@tonic-gate 2497c478bd9Sstevel@tonic-gate if ((retval = krb5_auth_con_init(kdc_context, &auth_context))) 2507c478bd9Sstevel@tonic-gate goto cleanup; 2517c478bd9Sstevel@tonic-gate 2527c478bd9Sstevel@tonic-gate if ((retval = krb5_auth_con_setaddrs(kdc_context, auth_context, NULL, 2537c478bd9Sstevel@tonic-gate from->address)) ) 2547c478bd9Sstevel@tonic-gate goto cleanup_auth_context; 2557c478bd9Sstevel@tonic-gate #ifdef USE_RCACHE 2567c478bd9Sstevel@tonic-gate if ((retval = krb5_auth_con_setrcache(kdc_context, auth_context, 2577c478bd9Sstevel@tonic-gate kdc_rcache))) 2587c478bd9Sstevel@tonic-gate goto cleanup_auth_context; 2597c478bd9Sstevel@tonic-gate #endif 2607c478bd9Sstevel@tonic-gate 2617c478bd9Sstevel@tonic-gate /* 2627c478bd9Sstevel@tonic-gate if ((retval = kdc_get_server_key(apreq->ticket, &key, &kvno))) 2637c478bd9Sstevel@tonic-gate goto cleanup_auth_context; 2647c478bd9Sstevel@tonic-gate */ 2657c478bd9Sstevel@tonic-gate 2667c478bd9Sstevel@tonic-gate /* 2677c478bd9Sstevel@tonic-gate * XXX This is currently wrong but to fix it will require making a 2687c478bd9Sstevel@tonic-gate * new keytab for groveling over the kdb. 2697c478bd9Sstevel@tonic-gate */ 2707c478bd9Sstevel@tonic-gate /* 2717c478bd9Sstevel@tonic-gate retval = krb5_auth_con_setuseruserkey(kdc_context, auth_context, key); 2727c478bd9Sstevel@tonic-gate krb5_free_keyblock(kdc_context, key); 2737c478bd9Sstevel@tonic-gate if (retval) 2747c478bd9Sstevel@tonic-gate goto cleanup_auth_context; 2757c478bd9Sstevel@tonic-gate */ 2767c478bd9Sstevel@tonic-gate 2777c478bd9Sstevel@tonic-gate if ((retval = krb5_rd_req_decoded_anyflag(kdc_context, &auth_context, apreq, 2787c478bd9Sstevel@tonic-gate apreq->ticket->server, 2797c478bd9Sstevel@tonic-gate kdc_active_realm->realm_keytab, 2807c478bd9Sstevel@tonic-gate NULL, ticket))) { 2817c478bd9Sstevel@tonic-gate #ifdef USE_RCACHE 2827c478bd9Sstevel@tonic-gate /* 2837c478bd9Sstevel@tonic-gate * I'm not so sure that this is right, but it's better than nothing 2847c478bd9Sstevel@tonic-gate * at all. 2857c478bd9Sstevel@tonic-gate * 2867c478bd9Sstevel@tonic-gate * If we choke in the rd_req because of the replay cache, then attempt 2877c478bd9Sstevel@tonic-gate * to reinitialize the replay cache because somebody could have deleted 2887c478bd9Sstevel@tonic-gate * it from underneath us (e.g. a cron job) 2897c478bd9Sstevel@tonic-gate */ 2907c478bd9Sstevel@tonic-gate if ((retval == KRB5_RC_IO_IO) || 2917c478bd9Sstevel@tonic-gate (retval == KRB5_RC_IO_UNKNOWN)) { 2927c478bd9Sstevel@tonic-gate (void) krb5_rc_close(kdc_context, kdc_rcache); 2937c478bd9Sstevel@tonic-gate kdc_rcache = (krb5_rcache) NULL; 2947c478bd9Sstevel@tonic-gate if (!(retval = kdc_initialize_rcache(kdc_context, (char *) NULL))) { 2957c478bd9Sstevel@tonic-gate if ((retval = krb5_auth_con_setrcache(kdc_context, auth_context, 2967c478bd9Sstevel@tonic-gate kdc_rcache)) || 2977c478bd9Sstevel@tonic-gate (retval = krb5_rd_req_decoded_anyflag(kdc_context, &auth_context, 2987c478bd9Sstevel@tonic-gate apreq, apreq->ticket->server, 2997c478bd9Sstevel@tonic-gate kdc_active_realm->realm_keytab, 3007c478bd9Sstevel@tonic-gate NULL, ticket)) 3017c478bd9Sstevel@tonic-gate ) 3027c478bd9Sstevel@tonic-gate goto cleanup_auth_context; 3037c478bd9Sstevel@tonic-gate } 3047c478bd9Sstevel@tonic-gate } else 3057c478bd9Sstevel@tonic-gate goto cleanup_auth_context; 3067c478bd9Sstevel@tonic-gate #else 3077c478bd9Sstevel@tonic-gate goto cleanup_auth_context; 3087c478bd9Sstevel@tonic-gate #endif 3097c478bd9Sstevel@tonic-gate } 3107c478bd9Sstevel@tonic-gate 3117c478bd9Sstevel@tonic-gate /* "invalid flag" tickets can must be used to validate */ 3127c478bd9Sstevel@tonic-gate if (isflagset((*ticket)->enc_part2->flags, TKT_FLG_INVALID) 3137c478bd9Sstevel@tonic-gate && !isflagset(request->kdc_options, KDC_OPT_VALIDATE)) { 3147c478bd9Sstevel@tonic-gate retval = KRB5KRB_AP_ERR_TKT_INVALID; 3157c478bd9Sstevel@tonic-gate goto cleanup_auth_context; 3167c478bd9Sstevel@tonic-gate } 3177c478bd9Sstevel@tonic-gate 31856a424ccSmp153739 if ((retval = krb5_auth_con_getrecvsubkey(kdc_context, 3197c478bd9Sstevel@tonic-gate auth_context, subkey))) 3207c478bd9Sstevel@tonic-gate goto cleanup_auth_context; 3217c478bd9Sstevel@tonic-gate 3227c478bd9Sstevel@tonic-gate if ((retval = krb5_auth_con_getauthenticator(kdc_context, auth_context, 3237c478bd9Sstevel@tonic-gate &authenticator))) 3247c478bd9Sstevel@tonic-gate goto cleanup_auth_context; 3257c478bd9Sstevel@tonic-gate 3267c478bd9Sstevel@tonic-gate /* Check for a checksum */ 3277c478bd9Sstevel@tonic-gate if (!(his_cksum = authenticator->checksum)) { 3287c478bd9Sstevel@tonic-gate retval = KRB5KRB_AP_ERR_INAPP_CKSUM; 3297c478bd9Sstevel@tonic-gate goto cleanup_authenticator; 3307c478bd9Sstevel@tonic-gate } 3317c478bd9Sstevel@tonic-gate 3327c478bd9Sstevel@tonic-gate /* make sure the client is of proper lineage (see above) */ 3337c478bd9Sstevel@tonic-gate if (foreign_server) { 3347c478bd9Sstevel@tonic-gate krb5_data *tkt_realm = krb5_princ_realm(kdc_context, 3357c478bd9Sstevel@tonic-gate (*ticket)->enc_part2->client); 3367c478bd9Sstevel@tonic-gate krb5_data *tgs_realm = krb5_princ_realm(kdc_context, tgs_server); 3377c478bd9Sstevel@tonic-gate if (tkt_realm->length == tgs_realm->length && 3387c478bd9Sstevel@tonic-gate !memcmp(tkt_realm->data, tgs_realm->data, tgs_realm->length)) { 3397c478bd9Sstevel@tonic-gate /* someone in a foreign realm claiming to be local */ 3407c478bd9Sstevel@tonic-gate krb5_klog_syslog(LOG_INFO, "PROCESS_TGS: failed lineage check"); 3417c478bd9Sstevel@tonic-gate retval = KRB5KDC_ERR_POLICY; 3427c478bd9Sstevel@tonic-gate goto cleanup_authenticator; 3437c478bd9Sstevel@tonic-gate } 3447c478bd9Sstevel@tonic-gate } 3457c478bd9Sstevel@tonic-gate 3467c478bd9Sstevel@tonic-gate /* 3477c478bd9Sstevel@tonic-gate * Check application checksum vs. tgs request 3487c478bd9Sstevel@tonic-gate * 3497c478bd9Sstevel@tonic-gate * We try checksumming the req-body two different ways: first we 3507c478bd9Sstevel@tonic-gate * try reaching into the raw asn.1 stream (if available), and 3517c478bd9Sstevel@tonic-gate * checksum that directly; if that fails, then we try encoding 3527c478bd9Sstevel@tonic-gate * using our local asn.1 library. 3537c478bd9Sstevel@tonic-gate */ 3547c478bd9Sstevel@tonic-gate if (pkt && (fetch_asn1_field((unsigned char *) pkt->data, 3557c478bd9Sstevel@tonic-gate 1, 4, &scratch1) >= 0)) { 3567c478bd9Sstevel@tonic-gate if (comp_cksum(kdc_context, &scratch1, *ticket, his_cksum)) { 3577c478bd9Sstevel@tonic-gate if (!(retval = encode_krb5_kdc_req_body(request, &scratch))) 3587c478bd9Sstevel@tonic-gate retval = comp_cksum(kdc_context, scratch, *ticket, his_cksum); 3597c478bd9Sstevel@tonic-gate krb5_free_data(kdc_context, scratch); 3607c478bd9Sstevel@tonic-gate } 3617c478bd9Sstevel@tonic-gate } 3627c478bd9Sstevel@tonic-gate 3637c478bd9Sstevel@tonic-gate cleanup_authenticator: 3647c478bd9Sstevel@tonic-gate krb5_free_authenticator(kdc_context, authenticator); 3657c478bd9Sstevel@tonic-gate 3667c478bd9Sstevel@tonic-gate cleanup_auth_context: 3677c478bd9Sstevel@tonic-gate /* We do not want the free of the auth_context to close the rcache */ 3687c478bd9Sstevel@tonic-gate #ifdef USE_RCACHE 3697c478bd9Sstevel@tonic-gate (void) krb5_auth_con_setrcache(kdc_context, auth_context, 0); 3707c478bd9Sstevel@tonic-gate #endif 3717c478bd9Sstevel@tonic-gate krb5_auth_con_free(kdc_context, auth_context); 3727c478bd9Sstevel@tonic-gate 3737c478bd9Sstevel@tonic-gate cleanup: 3747c478bd9Sstevel@tonic-gate krb5_free_ap_req(kdc_context, apreq); 3757c478bd9Sstevel@tonic-gate return retval; 3767c478bd9Sstevel@tonic-gate } 3777c478bd9Sstevel@tonic-gate 3787c478bd9Sstevel@tonic-gate /* XXX This function should no longer be necessary. 3797c478bd9Sstevel@tonic-gate * The KDC should take the keytab associated with the realm and pass that to 3807c478bd9Sstevel@tonic-gate * the krb5_rd_req_decode(). --proven 3817c478bd9Sstevel@tonic-gate * 3827c478bd9Sstevel@tonic-gate * It's actually still used by do_tgs_req() for u2u auth, and not too 3837c478bd9Sstevel@tonic-gate * much else. -- tlyu 3847c478bd9Sstevel@tonic-gate */ 3857c478bd9Sstevel@tonic-gate krb5_error_code 38656a424ccSmp153739 kdc_get_server_key(krb5_ticket *ticket, krb5_keyblock **key, krb5_kvno *kvno) 3877c478bd9Sstevel@tonic-gate { 3887c478bd9Sstevel@tonic-gate krb5_error_code retval; 3897c478bd9Sstevel@tonic-gate krb5_db_entry server; 3907c478bd9Sstevel@tonic-gate krb5_boolean more; 3917c478bd9Sstevel@tonic-gate int nprincs; 3927c478bd9Sstevel@tonic-gate krb5_key_data * server_key; 3937c478bd9Sstevel@tonic-gate 3947c478bd9Sstevel@tonic-gate nprincs = 1; 3957c478bd9Sstevel@tonic-gate 3967c478bd9Sstevel@tonic-gate if ((retval = krb5_db_get_principal(kdc_context, ticket->server, 3977c478bd9Sstevel@tonic-gate &server, &nprincs, 3987c478bd9Sstevel@tonic-gate &more))) { 3997c478bd9Sstevel@tonic-gate return(retval); 4007c478bd9Sstevel@tonic-gate } 4017c478bd9Sstevel@tonic-gate if (more) { 4027c478bd9Sstevel@tonic-gate krb5_db_free_principal(kdc_context, &server, nprincs); 4037c478bd9Sstevel@tonic-gate return(KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE); 4047c478bd9Sstevel@tonic-gate } else if (nprincs != 1) { 4057c478bd9Sstevel@tonic-gate char *sname; 4067c478bd9Sstevel@tonic-gate 4077c478bd9Sstevel@tonic-gate krb5_db_free_principal(kdc_context, &server, nprincs); 4087c478bd9Sstevel@tonic-gate if (!krb5_unparse_name(kdc_context, ticket->server, &sname)) { 40946736d35Ssemery limit_string(sname); 4107c478bd9Sstevel@tonic-gate krb5_klog_syslog(LOG_ERR,"TGS_REQ: UNKNOWN SERVER: server='%s'", 4117c478bd9Sstevel@tonic-gate sname); 4127c478bd9Sstevel@tonic-gate free(sname); 4137c478bd9Sstevel@tonic-gate } 4147c478bd9Sstevel@tonic-gate return(KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN); 4157c478bd9Sstevel@tonic-gate } 4167c478bd9Sstevel@tonic-gate retval = krb5_dbe_find_enctype(kdc_context, &server, 4177c478bd9Sstevel@tonic-gate ticket->enc_part.enctype, -1, 4187c478bd9Sstevel@tonic-gate ticket->enc_part.kvno, &server_key); 4197c478bd9Sstevel@tonic-gate if (retval) 4207c478bd9Sstevel@tonic-gate goto errout; 4217c478bd9Sstevel@tonic-gate if (!server_key) { 4227c478bd9Sstevel@tonic-gate retval = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; 4237c478bd9Sstevel@tonic-gate goto errout; 4247c478bd9Sstevel@tonic-gate } 4257c478bd9Sstevel@tonic-gate *kvno = server_key->key_data_kvno; 4267c478bd9Sstevel@tonic-gate if ((*key = (krb5_keyblock *)malloc(sizeof **key))) { 4277c478bd9Sstevel@tonic-gate retval = krb5_dbekd_decrypt_key_data(kdc_context, &master_keyblock, 4287c478bd9Sstevel@tonic-gate server_key, 4297c478bd9Sstevel@tonic-gate *key, NULL); 4307c478bd9Sstevel@tonic-gate } else 4317c478bd9Sstevel@tonic-gate retval = ENOMEM; 4327c478bd9Sstevel@tonic-gate errout: 4337c478bd9Sstevel@tonic-gate krb5_db_free_principal(kdc_context, &server, nprincs); 4347c478bd9Sstevel@tonic-gate return retval; 4357c478bd9Sstevel@tonic-gate } 4367c478bd9Sstevel@tonic-gate 4377c478bd9Sstevel@tonic-gate /* This probably wants to be updated if you support last_req stuff */ 4387c478bd9Sstevel@tonic-gate 4397c478bd9Sstevel@tonic-gate static krb5_last_req_entry nolrentry = { KV5M_LAST_REQ_ENTRY, KRB5_LRQ_NONE, 0 }; 4407c478bd9Sstevel@tonic-gate static krb5_last_req_entry *nolrarray[] = { &nolrentry, 0 }; 4417c478bd9Sstevel@tonic-gate 4427c478bd9Sstevel@tonic-gate krb5_error_code 44356a424ccSmp153739 fetch_last_req_info(krb5_db_entry *dbentry, krb5_last_req_entry ***lrentry) 4447c478bd9Sstevel@tonic-gate { 4457c478bd9Sstevel@tonic-gate *lrentry = nolrarray; 4467c478bd9Sstevel@tonic-gate return 0; 4477c478bd9Sstevel@tonic-gate } 4487c478bd9Sstevel@tonic-gate 4497c478bd9Sstevel@tonic-gate 4507c478bd9Sstevel@tonic-gate /* XXX! This is a temporary place-holder */ 4517c478bd9Sstevel@tonic-gate 4527c478bd9Sstevel@tonic-gate krb5_error_code 45356a424ccSmp153739 check_hot_list(krb5_ticket *ticket) 4547c478bd9Sstevel@tonic-gate { 4557c478bd9Sstevel@tonic-gate return 0; 4567c478bd9Sstevel@tonic-gate } 4577c478bd9Sstevel@tonic-gate 4587c478bd9Sstevel@tonic-gate 4597c478bd9Sstevel@tonic-gate #define MAX_REALM_LN 500 4607c478bd9Sstevel@tonic-gate 4617c478bd9Sstevel@tonic-gate 4627c478bd9Sstevel@tonic-gate /* 4637c478bd9Sstevel@tonic-gate * subrealm - determine if r2 is a subrealm of r1 4647c478bd9Sstevel@tonic-gate * 4657c478bd9Sstevel@tonic-gate * SUBREALM takes two realms, r1 and r2, and 4667c478bd9Sstevel@tonic-gate * determines if r2 is a subrealm of r1. 4677c478bd9Sstevel@tonic-gate * r2 is a subrealm of r1 if (r1 is a prefix 4687c478bd9Sstevel@tonic-gate * of r2 AND r1 and r2 begin with a /) or if 4697c478bd9Sstevel@tonic-gate * (r1 is a suffix of r2 and neither r1 nor r2 4707c478bd9Sstevel@tonic-gate * begin with a /). 4717c478bd9Sstevel@tonic-gate * 4727c478bd9Sstevel@tonic-gate * RETURNS: If r2 is a subrealm, and r1 is a prefix, the number 4737c478bd9Sstevel@tonic-gate * of characters in the suffix of r2 is returned as a 4747c478bd9Sstevel@tonic-gate * negative number. 4757c478bd9Sstevel@tonic-gate * 4767c478bd9Sstevel@tonic-gate * If r2 is a subrealm, and r1 is a suffix, the number 4777c478bd9Sstevel@tonic-gate * of characters in the prefix of r2 is returned as a 4787c478bd9Sstevel@tonic-gate * positive number. 4797c478bd9Sstevel@tonic-gate * 4807c478bd9Sstevel@tonic-gate * If r2 is not a subrealm, SUBREALM returns 0. 4817c478bd9Sstevel@tonic-gate */ 4827c478bd9Sstevel@tonic-gate static int 48356a424ccSmp153739 subrealm(char *r1, char *r2) 4847c478bd9Sstevel@tonic-gate { 48556a424ccSmp153739 size_t l1,l2; 4867c478bd9Sstevel@tonic-gate l1 = strlen(r1); 4877c478bd9Sstevel@tonic-gate l2 = strlen(r2); 4887c478bd9Sstevel@tonic-gate if(l2 <= l1) return(0); 4897c478bd9Sstevel@tonic-gate if((*r1 == '/') && (*r2 == '/') && (strncmp(r1,r2,l1) == 0)) return(l1-l2); 4907c478bd9Sstevel@tonic-gate if((*r1 != '/') && (*r2 != '/') && (strncmp(r1,r2+l2-l1,l1) == 0)) 4917c478bd9Sstevel@tonic-gate return(l2-l1); 4927c478bd9Sstevel@tonic-gate return(0); 4937c478bd9Sstevel@tonic-gate } 4947c478bd9Sstevel@tonic-gate 4957c478bd9Sstevel@tonic-gate /* 4967c478bd9Sstevel@tonic-gate * add_to_transited Adds the name of the realm which issued the 4977c478bd9Sstevel@tonic-gate * ticket granting ticket on which the new ticket to 4987c478bd9Sstevel@tonic-gate * be issued is based (note that this is the same as 4997c478bd9Sstevel@tonic-gate * the realm of the server listed in the ticket 5007c478bd9Sstevel@tonic-gate * granting ticket. 5017c478bd9Sstevel@tonic-gate * 5027c478bd9Sstevel@tonic-gate * ASSUMPTIONS: This procedure assumes that the transited field from 5037c478bd9Sstevel@tonic-gate * the existing ticket granting ticket already appears 5047c478bd9Sstevel@tonic-gate * in compressed form. It will add the new realm while 5057c478bd9Sstevel@tonic-gate * maintaining that form. As long as each successive 5067c478bd9Sstevel@tonic-gate * realm is added using this (or a similar) routine, the 5077c478bd9Sstevel@tonic-gate * transited field will be in compressed form. The 5087c478bd9Sstevel@tonic-gate * basis step is an empty transited field which is, by 5097c478bd9Sstevel@tonic-gate * its nature, in its most compressed form. 5107c478bd9Sstevel@tonic-gate * 5117c478bd9Sstevel@tonic-gate * ARGUMENTS: krb5_data *tgt_trans Transited field from TGT 5127c478bd9Sstevel@tonic-gate * krb5_data *new_trans The transited field for the new ticket 5137c478bd9Sstevel@tonic-gate * krb5_principal tgs Name of ticket granting server 5147c478bd9Sstevel@tonic-gate * This includes the realm of the KDC 5157c478bd9Sstevel@tonic-gate * that issued the ticket granting 5167c478bd9Sstevel@tonic-gate * ticket. This is the realm that is 5177c478bd9Sstevel@tonic-gate * to be added to the transited field. 5187c478bd9Sstevel@tonic-gate * krb5_principal client Name of the client 5197c478bd9Sstevel@tonic-gate * krb5_principal server The name of the requested server. 5207c478bd9Sstevel@tonic-gate * This may be the an intermediate 5217c478bd9Sstevel@tonic-gate * ticket granting server. 5227c478bd9Sstevel@tonic-gate * 5237c478bd9Sstevel@tonic-gate * The last two argument are needed since they are 5247c478bd9Sstevel@tonic-gate * implicitly part of the transited field of the new ticket 5257c478bd9Sstevel@tonic-gate * even though they are not explicitly listed. 5267c478bd9Sstevel@tonic-gate * 5277c478bd9Sstevel@tonic-gate * RETURNS: krb5_error_code - Success, or out of memory 5287c478bd9Sstevel@tonic-gate * 5297c478bd9Sstevel@tonic-gate * MODIFIES: new_trans: ->length will contain the length of the new 5307c478bd9Sstevel@tonic-gate * transited field. 5317c478bd9Sstevel@tonic-gate * 5327c478bd9Sstevel@tonic-gate * If ->data was not null when this procedure 5337c478bd9Sstevel@tonic-gate * is called, the memory referenced by ->data 5347c478bd9Sstevel@tonic-gate * will be deallocated. 5357c478bd9Sstevel@tonic-gate * 5367c478bd9Sstevel@tonic-gate * Memory will be allocated for the new transited field 5377c478bd9Sstevel@tonic-gate * ->data will be updated to point to the newly 5387c478bd9Sstevel@tonic-gate * allocated memory. 5397c478bd9Sstevel@tonic-gate * 5407c478bd9Sstevel@tonic-gate * BUGS: The space allocated for the new transited field is the 5417c478bd9Sstevel@tonic-gate * maximum that might be needed given the old transited field, 5427c478bd9Sstevel@tonic-gate * and the realm to be added. This length is calculated 5437c478bd9Sstevel@tonic-gate * assuming that no compression of the new realm is possible. 5447c478bd9Sstevel@tonic-gate * This has no adverse consequences other than the allocation 5457c478bd9Sstevel@tonic-gate * of more space than required. 5467c478bd9Sstevel@tonic-gate * 5477c478bd9Sstevel@tonic-gate * This procedure will not yet use the null subfield notation, 5487c478bd9Sstevel@tonic-gate * and it will get confused if it sees it. 5497c478bd9Sstevel@tonic-gate * 5507c478bd9Sstevel@tonic-gate * This procedure does not check for quoted commas in realm 5517c478bd9Sstevel@tonic-gate * names. 5527c478bd9Sstevel@tonic-gate */ 5537c478bd9Sstevel@tonic-gate 554*159d09a2SMark Phalan static char * 555*159d09a2SMark Phalan data2string (krb5_data *d) 556*159d09a2SMark Phalan { 557*159d09a2SMark Phalan char *s; 558*159d09a2SMark Phalan s = malloc(d->length + 1); 559*159d09a2SMark Phalan if (s) { 560*159d09a2SMark Phalan memcpy(s, d->data, d->length); 561*159d09a2SMark Phalan s[d->length] = 0; 562*159d09a2SMark Phalan } 563*159d09a2SMark Phalan return s; 564*159d09a2SMark Phalan } 565*159d09a2SMark Phalan 5667c478bd9Sstevel@tonic-gate krb5_error_code 56756a424ccSmp153739 add_to_transited(krb5_data *tgt_trans, krb5_data *new_trans, 56856a424ccSmp153739 krb5_principal tgs, krb5_principal client, 56956a424ccSmp153739 krb5_principal server) 5707c478bd9Sstevel@tonic-gate { 5717c478bd9Sstevel@tonic-gate krb5_error_code retval; 5727c478bd9Sstevel@tonic-gate char *realm; 5737c478bd9Sstevel@tonic-gate char *trans; 5747c478bd9Sstevel@tonic-gate char *otrans, *otrans_ptr; 5757c478bd9Sstevel@tonic-gate 5767c478bd9Sstevel@tonic-gate /* The following are for stepping through the transited field */ 5777c478bd9Sstevel@tonic-gate 5787c478bd9Sstevel@tonic-gate char prev[MAX_REALM_LN]; 5797c478bd9Sstevel@tonic-gate char next[MAX_REALM_LN]; 5807c478bd9Sstevel@tonic-gate char current[MAX_REALM_LN]; 5817c478bd9Sstevel@tonic-gate char exp[MAX_REALM_LN]; /* Expanded current realm name */ 5827c478bd9Sstevel@tonic-gate 5837c478bd9Sstevel@tonic-gate int i; 5847c478bd9Sstevel@tonic-gate int clst, nlst; /* count of last character in current and next */ 5857c478bd9Sstevel@tonic-gate int pl, pl1; /* prefix length */ 5867c478bd9Sstevel@tonic-gate int added; /* TRUE = new realm has been added */ 5877c478bd9Sstevel@tonic-gate 588*159d09a2SMark Phalan realm = data2string(krb5_princ_realm(kdc_context, tgs)); 589*159d09a2SMark Phalan if (realm == NULL) 5907c478bd9Sstevel@tonic-gate return(ENOMEM); 5917c478bd9Sstevel@tonic-gate 592*159d09a2SMark Phalan otrans = data2string(tgt_trans); 593*159d09a2SMark Phalan if (otrans == NULL) { 5947c478bd9Sstevel@tonic-gate free(realm); 5957c478bd9Sstevel@tonic-gate return(ENOMEM); 5967c478bd9Sstevel@tonic-gate } 5977c478bd9Sstevel@tonic-gate /* Keep track of start so we can free */ 5987c478bd9Sstevel@tonic-gate otrans_ptr = otrans; 5997c478bd9Sstevel@tonic-gate 6007c478bd9Sstevel@tonic-gate /* +1 for null, 6017c478bd9Sstevel@tonic-gate +1 for extra comma which may be added between 6027c478bd9Sstevel@tonic-gate +1 for potential space when leading slash in realm */ 6037c478bd9Sstevel@tonic-gate if (!(trans = (char *) malloc(strlen(realm) + strlen(otrans) + 3))) { 6047c478bd9Sstevel@tonic-gate retval = ENOMEM; 6057c478bd9Sstevel@tonic-gate goto fail; 6067c478bd9Sstevel@tonic-gate } 6077c478bd9Sstevel@tonic-gate 6087c478bd9Sstevel@tonic-gate if (new_trans->data) free(new_trans->data); 6097c478bd9Sstevel@tonic-gate new_trans->data = trans; 6107c478bd9Sstevel@tonic-gate new_trans->length = 0; 6117c478bd9Sstevel@tonic-gate 6127c478bd9Sstevel@tonic-gate trans[0] = '\0'; 6137c478bd9Sstevel@tonic-gate 6147c478bd9Sstevel@tonic-gate /* For the purpose of appending, the realm preceding the first */ 6157c478bd9Sstevel@tonic-gate /* realm in the transited field is considered the null realm */ 6167c478bd9Sstevel@tonic-gate 6177c478bd9Sstevel@tonic-gate prev[0] = '\0'; 6187c478bd9Sstevel@tonic-gate 6197c478bd9Sstevel@tonic-gate /* read field into current */ 6207c478bd9Sstevel@tonic-gate for (i = 0; *otrans != '\0';) { 62156a424ccSmp153739 if (*otrans == '\\') { 6227c478bd9Sstevel@tonic-gate if (*(++otrans) == '\0') 6237c478bd9Sstevel@tonic-gate break; 6247c478bd9Sstevel@tonic-gate else 6257c478bd9Sstevel@tonic-gate continue; 62656a424ccSmp153739 } 6277c478bd9Sstevel@tonic-gate if (*otrans == ',') { 6287c478bd9Sstevel@tonic-gate otrans++; 6297c478bd9Sstevel@tonic-gate break; 6307c478bd9Sstevel@tonic-gate } 6317c478bd9Sstevel@tonic-gate current[i++] = *otrans++; 6327c478bd9Sstevel@tonic-gate if (i >= MAX_REALM_LN) { 6337c478bd9Sstevel@tonic-gate retval = KRB5KRB_AP_ERR_ILL_CR_TKT; 6347c478bd9Sstevel@tonic-gate goto fail; 6357c478bd9Sstevel@tonic-gate } 6367c478bd9Sstevel@tonic-gate } 6377c478bd9Sstevel@tonic-gate current[i] = '\0'; 6387c478bd9Sstevel@tonic-gate 6397c478bd9Sstevel@tonic-gate added = (krb5_princ_realm(kdc_context, client)->length == strlen(realm) && 6407c478bd9Sstevel@tonic-gate !strncmp(krb5_princ_realm(kdc_context, client)->data, realm, strlen(realm))) || 6417c478bd9Sstevel@tonic-gate (krb5_princ_realm(kdc_context, server)->length == strlen(realm) && 6427c478bd9Sstevel@tonic-gate !strncmp(krb5_princ_realm(kdc_context, server)->data, realm, strlen(realm))); 6437c478bd9Sstevel@tonic-gate 6447c478bd9Sstevel@tonic-gate while (current[0]) { 6457c478bd9Sstevel@tonic-gate 6467c478bd9Sstevel@tonic-gate /* figure out expanded form of current name */ 6477c478bd9Sstevel@tonic-gate 6487c478bd9Sstevel@tonic-gate clst = strlen(current) - 1; 6497c478bd9Sstevel@tonic-gate if (current[0] == ' ') { 6507c478bd9Sstevel@tonic-gate strncpy(exp, current+1, sizeof(exp) - 1); 6517c478bd9Sstevel@tonic-gate exp[sizeof(exp) - 1] = '\0'; 6527c478bd9Sstevel@tonic-gate } 6537c478bd9Sstevel@tonic-gate else if ((current[0] == '/') && (prev[0] == '/')) { 6547c478bd9Sstevel@tonic-gate strncpy(exp, prev, sizeof(exp) - 1); 6557c478bd9Sstevel@tonic-gate exp[sizeof(exp) - 1] = '\0'; 6567c478bd9Sstevel@tonic-gate if (strlen(exp) + strlen(current) + 1 >= MAX_REALM_LN) { 6577c478bd9Sstevel@tonic-gate retval = KRB5KRB_AP_ERR_ILL_CR_TKT; 6587c478bd9Sstevel@tonic-gate goto fail; 6597c478bd9Sstevel@tonic-gate } 6607c478bd9Sstevel@tonic-gate strncat(exp, current, sizeof(exp) - 1 - strlen(exp)); 6617c478bd9Sstevel@tonic-gate } 6627c478bd9Sstevel@tonic-gate else if (current[clst] == '.') { 6637c478bd9Sstevel@tonic-gate strncpy(exp, current, sizeof(exp) - 1); 6647c478bd9Sstevel@tonic-gate exp[sizeof(exp) - 1] = '\0'; 6657c478bd9Sstevel@tonic-gate if (strlen(exp) + strlen(prev) + 1 >= MAX_REALM_LN) { 6667c478bd9Sstevel@tonic-gate retval = KRB5KRB_AP_ERR_ILL_CR_TKT; 6677c478bd9Sstevel@tonic-gate goto fail; 6687c478bd9Sstevel@tonic-gate } 6697c478bd9Sstevel@tonic-gate strncat(exp, prev, sizeof(exp) - 1 - strlen(exp)); 6707c478bd9Sstevel@tonic-gate } 6717c478bd9Sstevel@tonic-gate else { 6727c478bd9Sstevel@tonic-gate strncpy(exp, current, sizeof(exp) - 1); 6737c478bd9Sstevel@tonic-gate exp[sizeof(exp) - 1] = '\0'; 6747c478bd9Sstevel@tonic-gate } 6757c478bd9Sstevel@tonic-gate 6767c478bd9Sstevel@tonic-gate /* read field into next */ 6777c478bd9Sstevel@tonic-gate for (i = 0; *otrans != '\0';) { 67856a424ccSmp153739 if (*otrans == '\\') { 6797c478bd9Sstevel@tonic-gate if (*(++otrans) == '\0') 6807c478bd9Sstevel@tonic-gate break; 6817c478bd9Sstevel@tonic-gate else 6827c478bd9Sstevel@tonic-gate continue; 68356a424ccSmp153739 } 6847c478bd9Sstevel@tonic-gate if (*otrans == ',') { 6857c478bd9Sstevel@tonic-gate otrans++; 6867c478bd9Sstevel@tonic-gate break; 6877c478bd9Sstevel@tonic-gate } 6887c478bd9Sstevel@tonic-gate next[i++] = *otrans++; 6897c478bd9Sstevel@tonic-gate if (i >= MAX_REALM_LN) { 6907c478bd9Sstevel@tonic-gate retval = KRB5KRB_AP_ERR_ILL_CR_TKT; 6917c478bd9Sstevel@tonic-gate goto fail; 6927c478bd9Sstevel@tonic-gate } 6937c478bd9Sstevel@tonic-gate } 6947c478bd9Sstevel@tonic-gate next[i] = '\0'; 6957c478bd9Sstevel@tonic-gate nlst = i - 1; 6967c478bd9Sstevel@tonic-gate 6977c478bd9Sstevel@tonic-gate if (!strcmp(exp, realm)) added = TRUE; 6987c478bd9Sstevel@tonic-gate 6997c478bd9Sstevel@tonic-gate /* If we still have to insert the new realm */ 7007c478bd9Sstevel@tonic-gate 7017c478bd9Sstevel@tonic-gate if (!added) { 7027c478bd9Sstevel@tonic-gate 7037c478bd9Sstevel@tonic-gate /* Is the next field compressed? If not, and if the new */ 7047c478bd9Sstevel@tonic-gate /* realm is a subrealm of the current realm, compress */ 7057c478bd9Sstevel@tonic-gate /* the new realm, and insert immediately following the */ 7067c478bd9Sstevel@tonic-gate /* current one. Note that we can not do this if the next*/ 7077c478bd9Sstevel@tonic-gate /* field is already compressed since it would mess up */ 7087c478bd9Sstevel@tonic-gate /* what has already been done. In most cases, this is */ 7097c478bd9Sstevel@tonic-gate /* not a problem because the realm to be added will be a */ 7107c478bd9Sstevel@tonic-gate /* subrealm of the next field too, and we will catch */ 7117c478bd9Sstevel@tonic-gate /* it in a future iteration. */ 7127c478bd9Sstevel@tonic-gate 713*159d09a2SMark Phalan /* Note that the second test here is an unsigned comparison, 714*159d09a2SMark Phalan so the first half (or a cast) is also required. */ 715*159d09a2SMark Phalan assert(nlst < 0 || nlst < sizeof(next)); 716*159d09a2SMark Phalan if ((nlst < 0 || next[nlst] != '.') && 717*159d09a2SMark Phalan (next[0] != '/') && 7187c478bd9Sstevel@tonic-gate (pl = subrealm(exp, realm))) { 7197c478bd9Sstevel@tonic-gate added = TRUE; 7207c478bd9Sstevel@tonic-gate current[sizeof(current) - 1] = '\0'; 7217c478bd9Sstevel@tonic-gate if (strlen(current) + (pl>0?pl:-pl) + 2 >= MAX_REALM_LN) { 7227c478bd9Sstevel@tonic-gate retval = KRB5KRB_AP_ERR_ILL_CR_TKT; 7237c478bd9Sstevel@tonic-gate goto fail; 7247c478bd9Sstevel@tonic-gate } 7257c478bd9Sstevel@tonic-gate strncat(current, ",", sizeof(current) - 1 - strlen(current)); 7267c478bd9Sstevel@tonic-gate if (pl > 0) { 72756a424ccSmp153739 strncat(current, realm, (unsigned) pl); 7287c478bd9Sstevel@tonic-gate } 7297c478bd9Sstevel@tonic-gate else { 73056a424ccSmp153739 strncat(current, realm+strlen(realm)+pl, (unsigned) (-pl)); 7317c478bd9Sstevel@tonic-gate } 7327c478bd9Sstevel@tonic-gate } 7337c478bd9Sstevel@tonic-gate 7347c478bd9Sstevel@tonic-gate /* Whether or not the next field is compressed, if the */ 7357c478bd9Sstevel@tonic-gate /* realm to be added is a superrealm of the current realm,*/ 7367c478bd9Sstevel@tonic-gate /* then the current realm can be compressed. First the */ 7377c478bd9Sstevel@tonic-gate /* realm to be added must be compressed relative to the */ 7387c478bd9Sstevel@tonic-gate /* previous realm (if possible), and then the current */ 7397c478bd9Sstevel@tonic-gate /* realm compressed relative to the new realm. Note that */ 7407c478bd9Sstevel@tonic-gate /* if the realm to be added is also a superrealm of the */ 7417c478bd9Sstevel@tonic-gate /* previous realm, it would have been added earlier, and */ 7427c478bd9Sstevel@tonic-gate /* we would not reach this step this time around. */ 7437c478bd9Sstevel@tonic-gate 7447c478bd9Sstevel@tonic-gate else if ((pl = subrealm(realm, exp))) { 7457c478bd9Sstevel@tonic-gate added = TRUE; 7467c478bd9Sstevel@tonic-gate current[0] = '\0'; 7477c478bd9Sstevel@tonic-gate if ((pl1 = subrealm(prev,realm))) { 7487c478bd9Sstevel@tonic-gate if (strlen(current) + (pl1>0?pl1:-pl1) + 1 >= MAX_REALM_LN) { 7497c478bd9Sstevel@tonic-gate retval = KRB5KRB_AP_ERR_ILL_CR_TKT; 7507c478bd9Sstevel@tonic-gate goto fail; 7517c478bd9Sstevel@tonic-gate } 7527c478bd9Sstevel@tonic-gate if (pl1 > 0) { 75356a424ccSmp153739 strncat(current, realm, (unsigned) pl1); 7547c478bd9Sstevel@tonic-gate } 7557c478bd9Sstevel@tonic-gate else { 75656a424ccSmp153739 strncat(current, realm+strlen(realm)+pl1, (unsigned) (-pl1)); 7577c478bd9Sstevel@tonic-gate } 7587c478bd9Sstevel@tonic-gate } 7597c478bd9Sstevel@tonic-gate else { /* If not a subrealm */ 7607c478bd9Sstevel@tonic-gate if ((realm[0] == '/') && prev[0]) { 7617c478bd9Sstevel@tonic-gate if (strlen(current) + 2 >= MAX_REALM_LN) { 7627c478bd9Sstevel@tonic-gate retval = KRB5KRB_AP_ERR_ILL_CR_TKT; 7637c478bd9Sstevel@tonic-gate goto fail; 7647c478bd9Sstevel@tonic-gate } 7657c478bd9Sstevel@tonic-gate strncat(current, " ", sizeof(current) - 1 - strlen(current)); 7667c478bd9Sstevel@tonic-gate current[sizeof(current) - 1] = '\0'; 7677c478bd9Sstevel@tonic-gate } 7687c478bd9Sstevel@tonic-gate if (strlen(current) + strlen(realm) + 1 >= MAX_REALM_LN) { 7697c478bd9Sstevel@tonic-gate retval = KRB5KRB_AP_ERR_ILL_CR_TKT; 7707c478bd9Sstevel@tonic-gate goto fail; 7717c478bd9Sstevel@tonic-gate } 7727c478bd9Sstevel@tonic-gate strncat(current, realm, sizeof(current) - 1 - strlen(current)); 7737c478bd9Sstevel@tonic-gate current[sizeof(current) - 1] = '\0'; 7747c478bd9Sstevel@tonic-gate } 7757c478bd9Sstevel@tonic-gate if (strlen(current) + (pl>0?pl:-pl) + 2 >= MAX_REALM_LN) { 7767c478bd9Sstevel@tonic-gate retval = KRB5KRB_AP_ERR_ILL_CR_TKT; 7777c478bd9Sstevel@tonic-gate goto fail; 7787c478bd9Sstevel@tonic-gate } 7797c478bd9Sstevel@tonic-gate strncat(current,",", sizeof(current) - 1 - strlen(current)); 7807c478bd9Sstevel@tonic-gate current[sizeof(current) - 1] = '\0'; 7817c478bd9Sstevel@tonic-gate if (pl > 0) { 78256a424ccSmp153739 strncat(current, exp, (unsigned) pl); 7837c478bd9Sstevel@tonic-gate } 7847c478bd9Sstevel@tonic-gate else { 78556a424ccSmp153739 strncat(current, exp+strlen(exp)+pl, (unsigned)(-pl)); 7867c478bd9Sstevel@tonic-gate } 7877c478bd9Sstevel@tonic-gate } 7887c478bd9Sstevel@tonic-gate } 7897c478bd9Sstevel@tonic-gate 7907c478bd9Sstevel@tonic-gate if (new_trans->length != 0) { 7917c478bd9Sstevel@tonic-gate if (strlen(trans) + 2 >= MAX_REALM_LN) { 7927c478bd9Sstevel@tonic-gate retval = KRB5KRB_AP_ERR_ILL_CR_TKT; 7937c478bd9Sstevel@tonic-gate goto fail; 7947c478bd9Sstevel@tonic-gate } 7957c478bd9Sstevel@tonic-gate strcat(trans, ","); 7967c478bd9Sstevel@tonic-gate } 7977c478bd9Sstevel@tonic-gate if (strlen(trans) + strlen(current) + 1 >= MAX_REALM_LN) { 7987c478bd9Sstevel@tonic-gate retval = KRB5KRB_AP_ERR_ILL_CR_TKT; 7997c478bd9Sstevel@tonic-gate goto fail; 8007c478bd9Sstevel@tonic-gate } 8017c478bd9Sstevel@tonic-gate strcat(trans, current); 8027c478bd9Sstevel@tonic-gate new_trans->length = strlen(trans); 8037c478bd9Sstevel@tonic-gate 8047c478bd9Sstevel@tonic-gate strncpy(prev, exp, sizeof(prev) - 1); 8057c478bd9Sstevel@tonic-gate prev[sizeof(prev) - 1] = '\0'; 8067c478bd9Sstevel@tonic-gate strncpy(current, next, sizeof(current) - 1); 8077c478bd9Sstevel@tonic-gate current[sizeof(current) - 1] = '\0'; 8087c478bd9Sstevel@tonic-gate } 8097c478bd9Sstevel@tonic-gate 8107c478bd9Sstevel@tonic-gate if (!added) { 8117c478bd9Sstevel@tonic-gate if (new_trans->length != 0) { 8127c478bd9Sstevel@tonic-gate if (strlen(trans) + 2 >= MAX_REALM_LN) { 8137c478bd9Sstevel@tonic-gate retval = KRB5KRB_AP_ERR_ILL_CR_TKT; 8147c478bd9Sstevel@tonic-gate goto fail; 8157c478bd9Sstevel@tonic-gate } 8167c478bd9Sstevel@tonic-gate strcat(trans, ","); 8177c478bd9Sstevel@tonic-gate } 8187c478bd9Sstevel@tonic-gate if((realm[0] == '/') && trans[0]) { 8197c478bd9Sstevel@tonic-gate if (strlen(trans) + 2 >= MAX_REALM_LN) { 8207c478bd9Sstevel@tonic-gate retval = KRB5KRB_AP_ERR_ILL_CR_TKT; 8217c478bd9Sstevel@tonic-gate goto fail; 8227c478bd9Sstevel@tonic-gate } 8237c478bd9Sstevel@tonic-gate strcat(trans, " "); 8247c478bd9Sstevel@tonic-gate } 8257c478bd9Sstevel@tonic-gate if (strlen(trans) + strlen(realm) + 1 >= MAX_REALM_LN) { 8267c478bd9Sstevel@tonic-gate retval = KRB5KRB_AP_ERR_ILL_CR_TKT; 8277c478bd9Sstevel@tonic-gate goto fail; 8287c478bd9Sstevel@tonic-gate } 8297c478bd9Sstevel@tonic-gate strcat(trans, realm); 8307c478bd9Sstevel@tonic-gate new_trans->length = strlen(trans); 8317c478bd9Sstevel@tonic-gate } 8327c478bd9Sstevel@tonic-gate 8337c478bd9Sstevel@tonic-gate retval = 0; 8347c478bd9Sstevel@tonic-gate fail: 8357c478bd9Sstevel@tonic-gate free(realm); 8367c478bd9Sstevel@tonic-gate free(otrans_ptr); 8377c478bd9Sstevel@tonic-gate return (retval); 8387c478bd9Sstevel@tonic-gate } 8397c478bd9Sstevel@tonic-gate 8407c478bd9Sstevel@tonic-gate /* 8417c478bd9Sstevel@tonic-gate * Routines that validate a AS request; checks a lot of things. :-) 8427c478bd9Sstevel@tonic-gate * 8437c478bd9Sstevel@tonic-gate * Returns a Kerberos protocol error number, which is _not_ the same 8447c478bd9Sstevel@tonic-gate * as a com_err error number! 8457c478bd9Sstevel@tonic-gate */ 8467c478bd9Sstevel@tonic-gate #define AS_INVALID_OPTIONS (KDC_OPT_FORWARDED | KDC_OPT_PROXY |\ 8477c478bd9Sstevel@tonic-gate KDC_OPT_VALIDATE | KDC_OPT_RENEW | KDC_OPT_ENC_TKT_IN_SKEY) 8487c478bd9Sstevel@tonic-gate int 84956a424ccSmp153739 validate_as_request(register krb5_kdc_req *request, krb5_db_entry client, 85056a424ccSmp153739 krb5_db_entry server, krb5_timestamp kdc_time, 85156a424ccSmp153739 const char **status) 8527c478bd9Sstevel@tonic-gate { 8537c478bd9Sstevel@tonic-gate int errcode; 8547c478bd9Sstevel@tonic-gate 8557c478bd9Sstevel@tonic-gate /* 85656a424ccSmp153739 * If an option is set that is only allowed in TGS requests, complain. 8577c478bd9Sstevel@tonic-gate */ 8587c478bd9Sstevel@tonic-gate if (request->kdc_options & AS_INVALID_OPTIONS) { 8597c478bd9Sstevel@tonic-gate *status = "INVALID AS OPTIONS"; 8607c478bd9Sstevel@tonic-gate return KDC_ERR_BADOPTION; 8617c478bd9Sstevel@tonic-gate } 8627c478bd9Sstevel@tonic-gate 8637c478bd9Sstevel@tonic-gate /* The client's password must not be expired, unless the server is 8647c478bd9Sstevel@tonic-gate a KRB5_KDC_PWCHANGE_SERVICE. */ 8657c478bd9Sstevel@tonic-gate if (client.pw_expiration && client.pw_expiration < kdc_time && 8667c478bd9Sstevel@tonic-gate !isflagset(server.attributes, KRB5_KDB_PWCHANGE_SERVICE)) { 8677c478bd9Sstevel@tonic-gate *status = "CLIENT KEY EXPIRED"; 8687c478bd9Sstevel@tonic-gate #ifdef KRBCONF_VAGUE_ERRORS 8697c478bd9Sstevel@tonic-gate return(KRB_ERR_GENERIC); 8707c478bd9Sstevel@tonic-gate #else 8717c478bd9Sstevel@tonic-gate return(KDC_ERR_KEY_EXP); 8727c478bd9Sstevel@tonic-gate #endif 8737c478bd9Sstevel@tonic-gate } 8747c478bd9Sstevel@tonic-gate 8757c478bd9Sstevel@tonic-gate /* The client must not be expired */ 8767c478bd9Sstevel@tonic-gate if (client.expiration && client.expiration < kdc_time) { 8777c478bd9Sstevel@tonic-gate *status = "CLIENT EXPIRED"; 8787c478bd9Sstevel@tonic-gate #ifdef KRBCONF_VAGUE_ERRORS 8797c478bd9Sstevel@tonic-gate return(KRB_ERR_GENERIC); 8807c478bd9Sstevel@tonic-gate #else 8817c478bd9Sstevel@tonic-gate return(KDC_ERR_NAME_EXP); 8827c478bd9Sstevel@tonic-gate #endif 8837c478bd9Sstevel@tonic-gate } 8847c478bd9Sstevel@tonic-gate 8857c478bd9Sstevel@tonic-gate /* The server must not be expired */ 8867c478bd9Sstevel@tonic-gate if (server.expiration && server.expiration < kdc_time) { 8877c478bd9Sstevel@tonic-gate *status = "SERVICE EXPIRED"; 8887c478bd9Sstevel@tonic-gate return(KDC_ERR_SERVICE_EXP); 8897c478bd9Sstevel@tonic-gate } 8907c478bd9Sstevel@tonic-gate 8917c478bd9Sstevel@tonic-gate /* 8927c478bd9Sstevel@tonic-gate * If the client requires password changing, then only allow the 8937c478bd9Sstevel@tonic-gate * pwchange service. 8947c478bd9Sstevel@tonic-gate */ 8957c478bd9Sstevel@tonic-gate if (isflagset(client.attributes, KRB5_KDB_REQUIRES_PWCHANGE) && 8967c478bd9Sstevel@tonic-gate !isflagset(server.attributes, KRB5_KDB_PWCHANGE_SERVICE)) { 8977c478bd9Sstevel@tonic-gate *status = "REQUIRED PWCHANGE"; 8987c478bd9Sstevel@tonic-gate return(KDC_ERR_KEY_EXP); 8997c478bd9Sstevel@tonic-gate } 9007c478bd9Sstevel@tonic-gate 9017c478bd9Sstevel@tonic-gate /* Client and server must allow postdating tickets */ 9027c478bd9Sstevel@tonic-gate if ((isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE) || 9037c478bd9Sstevel@tonic-gate isflagset(request->kdc_options, KDC_OPT_POSTDATED)) && 9047c478bd9Sstevel@tonic-gate (isflagset(client.attributes, KRB5_KDB_DISALLOW_POSTDATED) || 9057c478bd9Sstevel@tonic-gate isflagset(server.attributes, KRB5_KDB_DISALLOW_POSTDATED))) { 9067c478bd9Sstevel@tonic-gate *status = "POSTDATE NOT ALLOWED"; 9077c478bd9Sstevel@tonic-gate return(KDC_ERR_CANNOT_POSTDATE); 9087c478bd9Sstevel@tonic-gate } 9097c478bd9Sstevel@tonic-gate 9107c478bd9Sstevel@tonic-gate /* Client and server must allow forwardable tickets */ 9117c478bd9Sstevel@tonic-gate if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE) && 9127c478bd9Sstevel@tonic-gate (isflagset(client.attributes, KRB5_KDB_DISALLOW_FORWARDABLE) || 9137c478bd9Sstevel@tonic-gate isflagset(server.attributes, KRB5_KDB_DISALLOW_FORWARDABLE))) { 9147c478bd9Sstevel@tonic-gate *status = "FORWARDABLE NOT ALLOWED"; 9157c478bd9Sstevel@tonic-gate return(KDC_ERR_POLICY); 9167c478bd9Sstevel@tonic-gate } 9177c478bd9Sstevel@tonic-gate 9187c478bd9Sstevel@tonic-gate /* Client and server must allow renewable tickets */ 9197c478bd9Sstevel@tonic-gate if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE) && 9207c478bd9Sstevel@tonic-gate (isflagset(client.attributes, KRB5_KDB_DISALLOW_RENEWABLE) || 9217c478bd9Sstevel@tonic-gate isflagset(server.attributes, KRB5_KDB_DISALLOW_RENEWABLE))) { 9227c478bd9Sstevel@tonic-gate *status = "RENEWABLE NOT ALLOWED"; 9237c478bd9Sstevel@tonic-gate return(KDC_ERR_POLICY); 9247c478bd9Sstevel@tonic-gate } 9257c478bd9Sstevel@tonic-gate 9267c478bd9Sstevel@tonic-gate /* Client and server must allow proxiable tickets */ 9277c478bd9Sstevel@tonic-gate if (isflagset(request->kdc_options, KDC_OPT_PROXIABLE) && 9287c478bd9Sstevel@tonic-gate (isflagset(client.attributes, KRB5_KDB_DISALLOW_PROXIABLE) || 9297c478bd9Sstevel@tonic-gate isflagset(server.attributes, KRB5_KDB_DISALLOW_PROXIABLE))) { 9307c478bd9Sstevel@tonic-gate *status = "PROXIABLE NOT ALLOWED"; 9317c478bd9Sstevel@tonic-gate return(KDC_ERR_POLICY); 9327c478bd9Sstevel@tonic-gate } 9337c478bd9Sstevel@tonic-gate 9347c478bd9Sstevel@tonic-gate /* Check to see if client is locked out */ 9357c478bd9Sstevel@tonic-gate if (isflagset(client.attributes, KRB5_KDB_DISALLOW_ALL_TIX)) { 9367c478bd9Sstevel@tonic-gate *status = "CLIENT LOCKED OUT"; 9377c478bd9Sstevel@tonic-gate return(KDC_ERR_C_PRINCIPAL_UNKNOWN); 9387c478bd9Sstevel@tonic-gate } 9397c478bd9Sstevel@tonic-gate 9407c478bd9Sstevel@tonic-gate /* Check to see if server is locked out */ 9417c478bd9Sstevel@tonic-gate if (isflagset(server.attributes, KRB5_KDB_DISALLOW_ALL_TIX)) { 9427c478bd9Sstevel@tonic-gate *status = "SERVICE LOCKED OUT"; 9437c478bd9Sstevel@tonic-gate return(KDC_ERR_S_PRINCIPAL_UNKNOWN); 9447c478bd9Sstevel@tonic-gate } 9457c478bd9Sstevel@tonic-gate 9467c478bd9Sstevel@tonic-gate /* Check to see if server is allowed to be a service */ 9477c478bd9Sstevel@tonic-gate if (isflagset(server.attributes, KRB5_KDB_DISALLOW_SVR)) { 9487c478bd9Sstevel@tonic-gate *status = "SERVICE NOT ALLOWED"; 9497c478bd9Sstevel@tonic-gate return(KDC_ERR_S_PRINCIPAL_UNKNOWN); 9507c478bd9Sstevel@tonic-gate } 9517c478bd9Sstevel@tonic-gate 9527c478bd9Sstevel@tonic-gate /* 9537c478bd9Sstevel@tonic-gate * Check against local policy 9547c478bd9Sstevel@tonic-gate */ 9557c478bd9Sstevel@tonic-gate errcode = against_local_policy_as(request, server, client, 9567c478bd9Sstevel@tonic-gate kdc_time, status); 9577c478bd9Sstevel@tonic-gate if (errcode) 9587c478bd9Sstevel@tonic-gate return errcode; 9597c478bd9Sstevel@tonic-gate 9607c478bd9Sstevel@tonic-gate return 0; 9617c478bd9Sstevel@tonic-gate } 9627c478bd9Sstevel@tonic-gate 9637c478bd9Sstevel@tonic-gate #define ASN1_ID_CLASS (0xc0) 9647c478bd9Sstevel@tonic-gate #define ASN1_ID_TYPE (0x20) 9657c478bd9Sstevel@tonic-gate #define ASN1_ID_TAG (0x1f) 9667c478bd9Sstevel@tonic-gate #define ASN1_CLASS_UNIV (0) 9677c478bd9Sstevel@tonic-gate #define ASN1_CLASS_APP (1) 9687c478bd9Sstevel@tonic-gate #define ASN1_CLASS_CTX (2) 9697c478bd9Sstevel@tonic-gate #define ASN1_CLASS_PRIV (3) 9707c478bd9Sstevel@tonic-gate #define asn1_id_constructed(x) (x & ASN1_ID_TYPE) 9717c478bd9Sstevel@tonic-gate #define asn1_id_primitive(x) (!asn1_id_constructed(x)) 9727c478bd9Sstevel@tonic-gate #define asn1_id_class(x) ((x & ASN1_ID_CLASS) >> 6) 9737c478bd9Sstevel@tonic-gate #define asn1_id_tag(x) (x & ASN1_ID_TAG) 9747c478bd9Sstevel@tonic-gate 9757c478bd9Sstevel@tonic-gate /* 9767c478bd9Sstevel@tonic-gate * asn1length - return encoded length of value. 9777c478bd9Sstevel@tonic-gate * 9787c478bd9Sstevel@tonic-gate * passed a pointer into the asn.1 stream, which is updated 9797c478bd9Sstevel@tonic-gate * to point right after the length bits. 9807c478bd9Sstevel@tonic-gate * 9817c478bd9Sstevel@tonic-gate * returns -1 on failure. 9827c478bd9Sstevel@tonic-gate */ 9837c478bd9Sstevel@tonic-gate static int 98456a424ccSmp153739 asn1length(unsigned char **astream) 9857c478bd9Sstevel@tonic-gate { 9867c478bd9Sstevel@tonic-gate int length; /* resulting length */ 9877c478bd9Sstevel@tonic-gate int sublen; /* sublengths */ 9887c478bd9Sstevel@tonic-gate int blen; /* bytes of length */ 9897c478bd9Sstevel@tonic-gate unsigned char *p; /* substring searching */ 9907c478bd9Sstevel@tonic-gate 9917c478bd9Sstevel@tonic-gate if (**astream & 0x80) { 9927c478bd9Sstevel@tonic-gate blen = **astream & 0x7f; 9937c478bd9Sstevel@tonic-gate if (blen > 3) { 9947c478bd9Sstevel@tonic-gate return(-1); 9957c478bd9Sstevel@tonic-gate } 9967c478bd9Sstevel@tonic-gate for (++*astream, length = 0; blen; ++*astream, blen--) { 9977c478bd9Sstevel@tonic-gate length = (length << 8) | **astream; 9987c478bd9Sstevel@tonic-gate } 9997c478bd9Sstevel@tonic-gate if (length == 0) { 10007c478bd9Sstevel@tonic-gate /* indefinite length, figure out by hand */ 10017c478bd9Sstevel@tonic-gate p = *astream; 10027c478bd9Sstevel@tonic-gate p++; 10037c478bd9Sstevel@tonic-gate while (1) { 10047c478bd9Sstevel@tonic-gate /* compute value length. */ 10057c478bd9Sstevel@tonic-gate if ((sublen = asn1length(&p)) < 0) { 10067c478bd9Sstevel@tonic-gate return(-1); 10077c478bd9Sstevel@tonic-gate } 10087c478bd9Sstevel@tonic-gate p += sublen; 10097c478bd9Sstevel@tonic-gate /* check for termination */ 10107c478bd9Sstevel@tonic-gate if ((!*p++) && (!*p)) { 10117c478bd9Sstevel@tonic-gate p++; 10127c478bd9Sstevel@tonic-gate break; 10137c478bd9Sstevel@tonic-gate } 10147c478bd9Sstevel@tonic-gate } 10157c478bd9Sstevel@tonic-gate length = p - *astream; 10167c478bd9Sstevel@tonic-gate } 10177c478bd9Sstevel@tonic-gate } else { 10187c478bd9Sstevel@tonic-gate length = **astream; 10197c478bd9Sstevel@tonic-gate ++*astream; 10207c478bd9Sstevel@tonic-gate } 10217c478bd9Sstevel@tonic-gate return(length); 10227c478bd9Sstevel@tonic-gate } 10237c478bd9Sstevel@tonic-gate 10247c478bd9Sstevel@tonic-gate /* 10257c478bd9Sstevel@tonic-gate * fetch_asn1_field - return raw asn.1 stream of subfield. 10267c478bd9Sstevel@tonic-gate * 10277c478bd9Sstevel@tonic-gate * this routine is passed a context-dependent tag number and "level" and returns 10287c478bd9Sstevel@tonic-gate * the size and length of the corresponding level subfield. 10297c478bd9Sstevel@tonic-gate * 10307c478bd9Sstevel@tonic-gate * levels and are numbered starting from 1. 10317c478bd9Sstevel@tonic-gate * 10327c478bd9Sstevel@tonic-gate * returns 0 on success, -1 otherwise. 10337c478bd9Sstevel@tonic-gate */ 10347c478bd9Sstevel@tonic-gate int 103556a424ccSmp153739 fetch_asn1_field(unsigned char *astream, unsigned int level, 103656a424ccSmp153739 unsigned int field, krb5_data *data) 10377c478bd9Sstevel@tonic-gate { 10387c478bd9Sstevel@tonic-gate unsigned char *estream; /* end of stream */ 10397c478bd9Sstevel@tonic-gate int classes; /* # classes seen so far this level */ 10407c478bd9Sstevel@tonic-gate unsigned int levels = 0; /* levels seen so far */ 10417c478bd9Sstevel@tonic-gate int lastlevel = 1000; /* last level seen */ 10427c478bd9Sstevel@tonic-gate int length; /* various lengths */ 10437c478bd9Sstevel@tonic-gate int tag; /* tag number */ 10447c478bd9Sstevel@tonic-gate unsigned char savelen; /* saved length of our field */ 10457c478bd9Sstevel@tonic-gate 10467c478bd9Sstevel@tonic-gate classes = -1; 10477c478bd9Sstevel@tonic-gate /* we assume that the first identifier/length will tell us 10487c478bd9Sstevel@tonic-gate how long the entire stream is. */ 10497c478bd9Sstevel@tonic-gate astream++; 10507c478bd9Sstevel@tonic-gate estream = astream; 10517c478bd9Sstevel@tonic-gate if ((length = asn1length(&astream)) < 0) { 10527c478bd9Sstevel@tonic-gate return(-1); 10537c478bd9Sstevel@tonic-gate } 10547c478bd9Sstevel@tonic-gate estream += length; 10557c478bd9Sstevel@tonic-gate /* search down the stream, checking identifiers. we process identifiers 10567c478bd9Sstevel@tonic-gate until we hit the "level" we want, and then process that level for our 10577c478bd9Sstevel@tonic-gate subfield, always making sure we don't go off the end of the stream. */ 10587c478bd9Sstevel@tonic-gate while (astream < estream) { 10597c478bd9Sstevel@tonic-gate if (!asn1_id_constructed(*astream)) { 10607c478bd9Sstevel@tonic-gate return(-1); 10617c478bd9Sstevel@tonic-gate } 10627c478bd9Sstevel@tonic-gate if (asn1_id_class(*astream) == ASN1_CLASS_CTX) { 10637c478bd9Sstevel@tonic-gate if ((tag = (int)asn1_id_tag(*astream)) <= lastlevel) { 10647c478bd9Sstevel@tonic-gate levels++; 10657c478bd9Sstevel@tonic-gate classes = -1; 10667c478bd9Sstevel@tonic-gate } 10677c478bd9Sstevel@tonic-gate lastlevel = tag; 10687c478bd9Sstevel@tonic-gate if (levels == level) { 10697c478bd9Sstevel@tonic-gate /* in our context-dependent class, is this the one we're looking for ? */ 10707c478bd9Sstevel@tonic-gate if (tag == field) { 10717c478bd9Sstevel@tonic-gate /* return length and data */ 10727c478bd9Sstevel@tonic-gate astream++; 10737c478bd9Sstevel@tonic-gate savelen = *astream; 10747c478bd9Sstevel@tonic-gate if ((data->length = asn1length(&astream)) < 0) { 10757c478bd9Sstevel@tonic-gate return(-1); 10767c478bd9Sstevel@tonic-gate } 10777c478bd9Sstevel@tonic-gate /* if the field length is indefinite, we will have to subtract two 10787c478bd9Sstevel@tonic-gate (terminating octets) from the length returned since we don't want 10797c478bd9Sstevel@tonic-gate to pass any info from the "wrapper" back. asn1length will always return 10807c478bd9Sstevel@tonic-gate the *total* length of the field, not just what's contained in it */ 10817c478bd9Sstevel@tonic-gate if ((savelen & 0xff) == 0x80) { 10827c478bd9Sstevel@tonic-gate data->length -=2 ; 10837c478bd9Sstevel@tonic-gate } 10847c478bd9Sstevel@tonic-gate data->data = (char *)astream; 10857c478bd9Sstevel@tonic-gate return(0); 10867c478bd9Sstevel@tonic-gate } else if (tag <= classes) { 10877c478bd9Sstevel@tonic-gate /* we've seen this class before, something must be wrong */ 10887c478bd9Sstevel@tonic-gate return(-1); 10897c478bd9Sstevel@tonic-gate } else { 10907c478bd9Sstevel@tonic-gate classes = tag; 10917c478bd9Sstevel@tonic-gate } 10927c478bd9Sstevel@tonic-gate } 10937c478bd9Sstevel@tonic-gate } 10947c478bd9Sstevel@tonic-gate /* if we're not on our level yet, process this value. otherwise skip over it */ 10957c478bd9Sstevel@tonic-gate astream++; 10967c478bd9Sstevel@tonic-gate if ((length = asn1length(&astream)) < 0) { 10977c478bd9Sstevel@tonic-gate return(-1); 10987c478bd9Sstevel@tonic-gate } 10997c478bd9Sstevel@tonic-gate if (levels == level) { 11007c478bd9Sstevel@tonic-gate astream += length; 11017c478bd9Sstevel@tonic-gate } 11027c478bd9Sstevel@tonic-gate } 11037c478bd9Sstevel@tonic-gate return(-1); 11047c478bd9Sstevel@tonic-gate } 11057c478bd9Sstevel@tonic-gate 11067c478bd9Sstevel@tonic-gate /* 11077c478bd9Sstevel@tonic-gate * Routines that validate a TGS request; checks a lot of things. :-) 11087c478bd9Sstevel@tonic-gate * 11097c478bd9Sstevel@tonic-gate * Returns a Kerberos protocol error number, which is _not_ the same 11107c478bd9Sstevel@tonic-gate * as a com_err error number! 11117c478bd9Sstevel@tonic-gate */ 11127c478bd9Sstevel@tonic-gate #define TGS_OPTIONS_HANDLED (KDC_OPT_FORWARDABLE | KDC_OPT_FORWARDED | \ 11137c478bd9Sstevel@tonic-gate KDC_OPT_PROXIABLE | KDC_OPT_PROXY | \ 11147c478bd9Sstevel@tonic-gate KDC_OPT_ALLOW_POSTDATE | KDC_OPT_POSTDATED | \ 11157c478bd9Sstevel@tonic-gate KDC_OPT_RENEWABLE | KDC_OPT_RENEWABLE_OK | \ 11167c478bd9Sstevel@tonic-gate KDC_OPT_ENC_TKT_IN_SKEY | KDC_OPT_RENEW | \ 11177c478bd9Sstevel@tonic-gate KDC_OPT_VALIDATE) 11187c478bd9Sstevel@tonic-gate 11197c478bd9Sstevel@tonic-gate #define NO_TGT_OPTION (KDC_OPT_FORWARDED | KDC_OPT_PROXY | KDC_OPT_RENEW | \ 11207c478bd9Sstevel@tonic-gate KDC_OPT_VALIDATE) 11217c478bd9Sstevel@tonic-gate 11227c478bd9Sstevel@tonic-gate int 112356a424ccSmp153739 validate_tgs_request(register krb5_kdc_req *request, krb5_db_entry server, 112456a424ccSmp153739 krb5_ticket *ticket, krb5_timestamp kdc_time, 112556a424ccSmp153739 const char **status) 11267c478bd9Sstevel@tonic-gate { 11277c478bd9Sstevel@tonic-gate int errcode; 11287c478bd9Sstevel@tonic-gate int st_idx = 0; 11297c478bd9Sstevel@tonic-gate 11307c478bd9Sstevel@tonic-gate /* 11317c478bd9Sstevel@tonic-gate * If an illegal option is set, ignore it. 11327c478bd9Sstevel@tonic-gate */ 113356a424ccSmp153739 request->kdc_options &= TGS_OPTIONS_HANDLED; 11347c478bd9Sstevel@tonic-gate 11357c478bd9Sstevel@tonic-gate /* Check to see if server has expired */ 11367c478bd9Sstevel@tonic-gate if (server.expiration && server.expiration < kdc_time) { 11377c478bd9Sstevel@tonic-gate *status = "SERVICE EXPIRED"; 11387c478bd9Sstevel@tonic-gate return(KDC_ERR_SERVICE_EXP); 11397c478bd9Sstevel@tonic-gate } 11407c478bd9Sstevel@tonic-gate 11417c478bd9Sstevel@tonic-gate /* 11427c478bd9Sstevel@tonic-gate * Verify that the server principal in authdat->ticket is correct 11437c478bd9Sstevel@tonic-gate * (either the ticket granting service or the service that was 11447c478bd9Sstevel@tonic-gate * originally requested) 11457c478bd9Sstevel@tonic-gate */ 11467c478bd9Sstevel@tonic-gate if (request->kdc_options & NO_TGT_OPTION) { 11477c478bd9Sstevel@tonic-gate if (!krb5_principal_compare(kdc_context, ticket->server, request->server)) { 11487c478bd9Sstevel@tonic-gate *status = "SERVER DIDN'T MATCH TICKET FOR RENEW/FORWARD/ETC"; 11497c478bd9Sstevel@tonic-gate return(KDC_ERR_SERVER_NOMATCH); 11507c478bd9Sstevel@tonic-gate } 11517c478bd9Sstevel@tonic-gate } else { 11527c478bd9Sstevel@tonic-gate /* 11537c478bd9Sstevel@tonic-gate * OK, we need to validate the krbtgt service in the ticket. 11547c478bd9Sstevel@tonic-gate * 11557c478bd9Sstevel@tonic-gate * The krbtgt service is of the form: 11567c478bd9Sstevel@tonic-gate * krbtgt/realm-A@realm-B 11577c478bd9Sstevel@tonic-gate * 11587c478bd9Sstevel@tonic-gate * Realm A is the "server realm"; the realm of the 11597c478bd9Sstevel@tonic-gate * server of the requested ticket must match this realm. 11607c478bd9Sstevel@tonic-gate * Of course, it should be a realm serviced by this KDC. 11617c478bd9Sstevel@tonic-gate * 11627c478bd9Sstevel@tonic-gate * Realm B is the "client realm"; this is what should be 11637c478bd9Sstevel@tonic-gate * added to the transited field. (which is done elsewhere) 11647c478bd9Sstevel@tonic-gate */ 11657c478bd9Sstevel@tonic-gate 11667c478bd9Sstevel@tonic-gate /* Make sure there are two components... */ 11677c478bd9Sstevel@tonic-gate if (krb5_princ_size(kdc_context, ticket->server) != 2) { 11687c478bd9Sstevel@tonic-gate *status = "BAD TGS SERVER LENGTH"; 11697c478bd9Sstevel@tonic-gate return KRB_AP_ERR_NOT_US; 11707c478bd9Sstevel@tonic-gate } 11717c478bd9Sstevel@tonic-gate /* ...that the first component is krbtgt... */ 11727c478bd9Sstevel@tonic-gate if (!krb5_is_tgs_principal(ticket->server)) { 11737c478bd9Sstevel@tonic-gate *status = "BAD TGS SERVER NAME"; 11747c478bd9Sstevel@tonic-gate return KRB_AP_ERR_NOT_US; 11757c478bd9Sstevel@tonic-gate } 11767c478bd9Sstevel@tonic-gate /* ...and that the second component matches the server realm... */ 117756a424ccSmp153739 if ((krb5_princ_size(kdc_context, ticket->server) <= 1) || 117856a424ccSmp153739 (krb5_princ_component(kdc_context, ticket->server, 1)->length != 11797c478bd9Sstevel@tonic-gate krb5_princ_realm(kdc_context, request->server)->length) || 11807c478bd9Sstevel@tonic-gate memcmp(krb5_princ_component(kdc_context, ticket->server, 1)->data, 11817c478bd9Sstevel@tonic-gate krb5_princ_realm(kdc_context, request->server)->data, 11827c478bd9Sstevel@tonic-gate krb5_princ_realm(kdc_context, request->server)->length)) { 11837c478bd9Sstevel@tonic-gate *status = "BAD TGS SERVER INSTANCE"; 11847c478bd9Sstevel@tonic-gate return KRB_AP_ERR_NOT_US; 11857c478bd9Sstevel@tonic-gate } 11867c478bd9Sstevel@tonic-gate /* XXX add check that second component must match locally 11877c478bd9Sstevel@tonic-gate * supported realm? 11887c478bd9Sstevel@tonic-gate */ 11897c478bd9Sstevel@tonic-gate 11907c478bd9Sstevel@tonic-gate /* Server must allow TGS based issuances */ 11917c478bd9Sstevel@tonic-gate if (isflagset(server.attributes, KRB5_KDB_DISALLOW_TGT_BASED)) { 11927c478bd9Sstevel@tonic-gate *status = "TGT BASED NOT ALLOWED"; 11937c478bd9Sstevel@tonic-gate return(KDC_ERR_POLICY); 11947c478bd9Sstevel@tonic-gate } 11957c478bd9Sstevel@tonic-gate } 11967c478bd9Sstevel@tonic-gate 11977c478bd9Sstevel@tonic-gate /* TGS must be forwardable to get forwarded or forwardable ticket */ 11987c478bd9Sstevel@tonic-gate if ((isflagset(request->kdc_options, KDC_OPT_FORWARDED) || 11997c478bd9Sstevel@tonic-gate isflagset(request->kdc_options, KDC_OPT_FORWARDABLE)) && 12007c478bd9Sstevel@tonic-gate !isflagset(ticket->enc_part2->flags, TKT_FLG_FORWARDABLE)) { 12017c478bd9Sstevel@tonic-gate *status = "TGT NOT FORWARDABLE"; 12027c478bd9Sstevel@tonic-gate 12037c478bd9Sstevel@tonic-gate return KDC_ERR_BADOPTION; 12047c478bd9Sstevel@tonic-gate } 12057c478bd9Sstevel@tonic-gate 12067c478bd9Sstevel@tonic-gate /* TGS must be proxiable to get proxiable ticket */ 12077c478bd9Sstevel@tonic-gate if ((isflagset(request->kdc_options, KDC_OPT_PROXY) || 12087c478bd9Sstevel@tonic-gate isflagset(request->kdc_options, KDC_OPT_PROXIABLE)) && 12097c478bd9Sstevel@tonic-gate !isflagset(ticket->enc_part2->flags, TKT_FLG_PROXIABLE)) { 12107c478bd9Sstevel@tonic-gate *status = "TGT NOT PROXIABLE"; 12117c478bd9Sstevel@tonic-gate return KDC_ERR_BADOPTION; 12127c478bd9Sstevel@tonic-gate } 12137c478bd9Sstevel@tonic-gate 12147c478bd9Sstevel@tonic-gate /* TGS must allow postdating to get postdated ticket */ 12157c478bd9Sstevel@tonic-gate if ((isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE) || 12167c478bd9Sstevel@tonic-gate isflagset(request->kdc_options, KDC_OPT_POSTDATED)) && 12177c478bd9Sstevel@tonic-gate !isflagset(ticket->enc_part2->flags, TKT_FLG_MAY_POSTDATE)) { 12187c478bd9Sstevel@tonic-gate *status = "TGT NOT POSTDATABLE"; 12197c478bd9Sstevel@tonic-gate return KDC_ERR_BADOPTION; 12207c478bd9Sstevel@tonic-gate } 12217c478bd9Sstevel@tonic-gate 12227c478bd9Sstevel@tonic-gate /* can only validate invalid tix */ 12237c478bd9Sstevel@tonic-gate if (isflagset(request->kdc_options, KDC_OPT_VALIDATE) && 12247c478bd9Sstevel@tonic-gate !isflagset(ticket->enc_part2->flags, TKT_FLG_INVALID)) { 12257c478bd9Sstevel@tonic-gate *status = "VALIDATE VALID TICKET"; 12267c478bd9Sstevel@tonic-gate return KDC_ERR_BADOPTION; 12277c478bd9Sstevel@tonic-gate } 12287c478bd9Sstevel@tonic-gate 12297c478bd9Sstevel@tonic-gate /* can only renew renewable tix */ 12307c478bd9Sstevel@tonic-gate if ((isflagset(request->kdc_options, KDC_OPT_RENEW) || 12317c478bd9Sstevel@tonic-gate isflagset(request->kdc_options, KDC_OPT_RENEWABLE)) && 12327c478bd9Sstevel@tonic-gate !isflagset(ticket->enc_part2->flags, TKT_FLG_RENEWABLE)) { 12337c478bd9Sstevel@tonic-gate *status = "TICKET NOT RENEWABLE"; 12347c478bd9Sstevel@tonic-gate return KDC_ERR_BADOPTION; 12357c478bd9Sstevel@tonic-gate } 12367c478bd9Sstevel@tonic-gate 12377c478bd9Sstevel@tonic-gate /* can not proxy ticket granting tickets */ 12387c478bd9Sstevel@tonic-gate if (isflagset(request->kdc_options, KDC_OPT_PROXY) && 12397c478bd9Sstevel@tonic-gate (!request->server->data || 12407c478bd9Sstevel@tonic-gate request->server->data[0].length != KRB5_TGS_NAME_SIZE || 12417c478bd9Sstevel@tonic-gate memcmp(request->server->data[0].data, KRB5_TGS_NAME, 12427c478bd9Sstevel@tonic-gate KRB5_TGS_NAME_SIZE))) { 12437c478bd9Sstevel@tonic-gate *status = "CAN'T PROXY TGT"; 12447c478bd9Sstevel@tonic-gate return KDC_ERR_BADOPTION; 12457c478bd9Sstevel@tonic-gate } 12467c478bd9Sstevel@tonic-gate 12477c478bd9Sstevel@tonic-gate /* Server must allow forwardable tickets */ 12487c478bd9Sstevel@tonic-gate if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE) && 12497c478bd9Sstevel@tonic-gate isflagset(server.attributes, KRB5_KDB_DISALLOW_FORWARDABLE)) { 12507c478bd9Sstevel@tonic-gate *status = "NON-FORWARDABLE TICKET"; 12517c478bd9Sstevel@tonic-gate return(KDC_ERR_POLICY); 12527c478bd9Sstevel@tonic-gate } 12537c478bd9Sstevel@tonic-gate 12547c478bd9Sstevel@tonic-gate /* Server must allow renewable tickets */ 12557c478bd9Sstevel@tonic-gate if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE) && 12567c478bd9Sstevel@tonic-gate isflagset(server.attributes, KRB5_KDB_DISALLOW_RENEWABLE)) { 12577c478bd9Sstevel@tonic-gate *status = "NON-RENEWABLE TICKET"; 12587c478bd9Sstevel@tonic-gate return(KDC_ERR_POLICY); 12597c478bd9Sstevel@tonic-gate } 12607c478bd9Sstevel@tonic-gate 12617c478bd9Sstevel@tonic-gate /* Server must allow proxiable tickets */ 12627c478bd9Sstevel@tonic-gate if (isflagset(request->kdc_options, KDC_OPT_PROXIABLE) && 12637c478bd9Sstevel@tonic-gate isflagset(server.attributes, KRB5_KDB_DISALLOW_PROXIABLE)) { 12647c478bd9Sstevel@tonic-gate *status = "NON-PROXIABLE TICKET"; 12657c478bd9Sstevel@tonic-gate return(KDC_ERR_POLICY); 12667c478bd9Sstevel@tonic-gate } 12677c478bd9Sstevel@tonic-gate 12687c478bd9Sstevel@tonic-gate /* Server must allow postdated tickets */ 12697c478bd9Sstevel@tonic-gate if (isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE) && 12707c478bd9Sstevel@tonic-gate isflagset(server.attributes, KRB5_KDB_DISALLOW_POSTDATED)) { 12717c478bd9Sstevel@tonic-gate *status = "NON-POSTDATABLE TICKET"; 12727c478bd9Sstevel@tonic-gate return(KDC_ERR_CANNOT_POSTDATE); 12737c478bd9Sstevel@tonic-gate } 12747c478bd9Sstevel@tonic-gate 12757c478bd9Sstevel@tonic-gate /* Server must allow DUP SKEY requests */ 12767c478bd9Sstevel@tonic-gate if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY) && 12777c478bd9Sstevel@tonic-gate isflagset(server.attributes, KRB5_KDB_DISALLOW_DUP_SKEY)) { 12787c478bd9Sstevel@tonic-gate *status = "DUP_SKEY DISALLOWED"; 12797c478bd9Sstevel@tonic-gate return(KDC_ERR_POLICY); 12807c478bd9Sstevel@tonic-gate } 12817c478bd9Sstevel@tonic-gate 12827c478bd9Sstevel@tonic-gate /* Server must not be locked out */ 12837c478bd9Sstevel@tonic-gate if (isflagset(server.attributes, KRB5_KDB_DISALLOW_ALL_TIX)) { 12847c478bd9Sstevel@tonic-gate *status = "SERVER LOCKED OUT"; 12857c478bd9Sstevel@tonic-gate return(KDC_ERR_S_PRINCIPAL_UNKNOWN); 12867c478bd9Sstevel@tonic-gate } 12877c478bd9Sstevel@tonic-gate 12887c478bd9Sstevel@tonic-gate /* Server must be allowed to be a service */ 12897c478bd9Sstevel@tonic-gate if (isflagset(server.attributes, KRB5_KDB_DISALLOW_SVR)) { 12907c478bd9Sstevel@tonic-gate *status = "SERVER NOT ALLOWED"; 12917c478bd9Sstevel@tonic-gate return(KDC_ERR_S_PRINCIPAL_UNKNOWN); 12927c478bd9Sstevel@tonic-gate } 12937c478bd9Sstevel@tonic-gate 12947c478bd9Sstevel@tonic-gate /* Check the hot list */ 12957c478bd9Sstevel@tonic-gate if (check_hot_list(ticket)) { 12967c478bd9Sstevel@tonic-gate *status = "HOT_LIST"; 12977c478bd9Sstevel@tonic-gate return(KRB_AP_ERR_REPEAT); 12987c478bd9Sstevel@tonic-gate } 12997c478bd9Sstevel@tonic-gate 13007c478bd9Sstevel@tonic-gate /* Check the start time vs. the KDC time */ 13017c478bd9Sstevel@tonic-gate if (isflagset(request->kdc_options, KDC_OPT_VALIDATE)) { 13027c478bd9Sstevel@tonic-gate if (ticket->enc_part2->times.starttime > kdc_time) { 13037c478bd9Sstevel@tonic-gate *status = "NOT_YET_VALID"; 13047c478bd9Sstevel@tonic-gate return(KRB_AP_ERR_TKT_NYV); 13057c478bd9Sstevel@tonic-gate } 13067c478bd9Sstevel@tonic-gate } 13077c478bd9Sstevel@tonic-gate 13087c478bd9Sstevel@tonic-gate /* 13097c478bd9Sstevel@tonic-gate * Check the renew_till time. The endtime was already 13107c478bd9Sstevel@tonic-gate * been checked in the initial authentication check. 13117c478bd9Sstevel@tonic-gate */ 13127c478bd9Sstevel@tonic-gate if (isflagset(request->kdc_options, KDC_OPT_RENEW) && 13137c478bd9Sstevel@tonic-gate (ticket->enc_part2->times.renew_till < kdc_time)) { 13147c478bd9Sstevel@tonic-gate *status = "TKT_EXPIRED"; 13157c478bd9Sstevel@tonic-gate return(KRB_AP_ERR_TKT_EXPIRED); 13167c478bd9Sstevel@tonic-gate } 13177c478bd9Sstevel@tonic-gate 13187c478bd9Sstevel@tonic-gate /* 13197c478bd9Sstevel@tonic-gate * Checks for ENC_TKT_IN_SKEY: 13207c478bd9Sstevel@tonic-gate * 13217c478bd9Sstevel@tonic-gate * (1) Make sure the second ticket exists 13227c478bd9Sstevel@tonic-gate * (2) Make sure it is a ticket granting ticket 13237c478bd9Sstevel@tonic-gate */ 13247c478bd9Sstevel@tonic-gate if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY)) { 13257c478bd9Sstevel@tonic-gate if (!request->second_ticket || 13267c478bd9Sstevel@tonic-gate !request->second_ticket[st_idx]) { 13277c478bd9Sstevel@tonic-gate *status = "NO_2ND_TKT"; 13287c478bd9Sstevel@tonic-gate return(KDC_ERR_BADOPTION); 13297c478bd9Sstevel@tonic-gate } 13307c478bd9Sstevel@tonic-gate if (!krb5_principal_compare(kdc_context, request->second_ticket[st_idx]->server, 13317c478bd9Sstevel@tonic-gate tgs_server)) { 13327c478bd9Sstevel@tonic-gate *status = "2ND_TKT_NOT_TGS"; 13337c478bd9Sstevel@tonic-gate return(KDC_ERR_POLICY); 13347c478bd9Sstevel@tonic-gate } 13357c478bd9Sstevel@tonic-gate st_idx++; 13367c478bd9Sstevel@tonic-gate } 13377c478bd9Sstevel@tonic-gate 13387c478bd9Sstevel@tonic-gate /* Check for hardware preauthentication */ 13397c478bd9Sstevel@tonic-gate if (isflagset(server.attributes, KRB5_KDB_REQUIRES_HW_AUTH) && 13407c478bd9Sstevel@tonic-gate !isflagset(ticket->enc_part2->flags,TKT_FLG_HW_AUTH)) { 13417c478bd9Sstevel@tonic-gate *status = "NO HW PREAUTH"; 13427c478bd9Sstevel@tonic-gate return KRB_ERR_GENERIC; 13437c478bd9Sstevel@tonic-gate } 13447c478bd9Sstevel@tonic-gate 13457c478bd9Sstevel@tonic-gate /* Check for any kind of preauthentication */ 13467c478bd9Sstevel@tonic-gate if (isflagset(server.attributes, KRB5_KDB_REQUIRES_PRE_AUTH) && 13477c478bd9Sstevel@tonic-gate !isflagset(ticket->enc_part2->flags, TKT_FLG_PRE_AUTH)) { 13487c478bd9Sstevel@tonic-gate *status = "NO PREAUTH"; 13497c478bd9Sstevel@tonic-gate return KRB_ERR_GENERIC; 13507c478bd9Sstevel@tonic-gate } 13517c478bd9Sstevel@tonic-gate 13527c478bd9Sstevel@tonic-gate /* 13537c478bd9Sstevel@tonic-gate * Check local policy 13547c478bd9Sstevel@tonic-gate */ 13557c478bd9Sstevel@tonic-gate errcode = against_local_policy_tgs(request, server, ticket, status); 13567c478bd9Sstevel@tonic-gate if (errcode) 13577c478bd9Sstevel@tonic-gate return errcode; 13587c478bd9Sstevel@tonic-gate 13597c478bd9Sstevel@tonic-gate 13607c478bd9Sstevel@tonic-gate return 0; 13617c478bd9Sstevel@tonic-gate } 13627c478bd9Sstevel@tonic-gate 13637c478bd9Sstevel@tonic-gate /* 13647c478bd9Sstevel@tonic-gate * This function returns 1 if the dbentry has a key for a specified 13657c478bd9Sstevel@tonic-gate * keytype, and 0 if not. 13667c478bd9Sstevel@tonic-gate */ 13677c478bd9Sstevel@tonic-gate int 136856a424ccSmp153739 dbentry_has_key_for_enctype(krb5_context context, krb5_db_entry *client, 136956a424ccSmp153739 krb5_enctype enctype) 13707c478bd9Sstevel@tonic-gate { 13717c478bd9Sstevel@tonic-gate krb5_error_code retval; 13727c478bd9Sstevel@tonic-gate krb5_key_data *datap; 13737c478bd9Sstevel@tonic-gate 13747c478bd9Sstevel@tonic-gate retval = krb5_dbe_find_enctype(context, client, enctype, 13757c478bd9Sstevel@tonic-gate -1, 0, &datap); 13767c478bd9Sstevel@tonic-gate if (retval) 13777c478bd9Sstevel@tonic-gate return 0; 13787c478bd9Sstevel@tonic-gate else 13797c478bd9Sstevel@tonic-gate return 1; 13807c478bd9Sstevel@tonic-gate } 13817c478bd9Sstevel@tonic-gate 13827c478bd9Sstevel@tonic-gate /* 13837c478bd9Sstevel@tonic-gate * This function returns 1 if the entity referenced by this 13847c478bd9Sstevel@tonic-gate * structure can support the a particular encryption system, and 0 if 13857c478bd9Sstevel@tonic-gate * not. 13867c478bd9Sstevel@tonic-gate * 13877c478bd9Sstevel@tonic-gate * XXX eventually this information should be looked up in the 13887c478bd9Sstevel@tonic-gate * database. Since it isn't, we use some hueristics and attribute 13897c478bd9Sstevel@tonic-gate * options bits for now. 13907c478bd9Sstevel@tonic-gate */ 13917c478bd9Sstevel@tonic-gate int 139256a424ccSmp153739 dbentry_supports_enctype(krb5_context context, krb5_db_entry *client, 139356a424ccSmp153739 krb5_enctype enctype) 13947c478bd9Sstevel@tonic-gate { 13957c478bd9Sstevel@tonic-gate /* 13967c478bd9Sstevel@tonic-gate * If it's DES_CBC_MD5, there's a bit in the attribute mask which 13977c478bd9Sstevel@tonic-gate * checks to see if we support it. 13987c478bd9Sstevel@tonic-gate * 13997c478bd9Sstevel@tonic-gate * In theory everything's supposed to support DES_CBC_MD5, but 14007c478bd9Sstevel@tonic-gate * that's not the reality.... 14017c478bd9Sstevel@tonic-gate */ 14027c478bd9Sstevel@tonic-gate 14037c478bd9Sstevel@tonic-gate /* 14047c478bd9Sstevel@tonic-gate * We are assuming that all entries can support MD5; this information 14057c478bd9Sstevel@tonic-gate * need not be kept in the database. 14067c478bd9Sstevel@tonic-gate */ 14077c478bd9Sstevel@tonic-gate 14087c478bd9Sstevel@tonic-gate 14097c478bd9Sstevel@tonic-gate if (enctype == ENCTYPE_DES_CBC_MD5) 14107c478bd9Sstevel@tonic-gate return 1; 14117c478bd9Sstevel@tonic-gate 14127c478bd9Sstevel@tonic-gate /* 14137c478bd9Sstevel@tonic-gate * XXX we assume everything can understand DES_CBC_CRC 14147c478bd9Sstevel@tonic-gate */ 14157c478bd9Sstevel@tonic-gate if (enctype == ENCTYPE_DES_CBC_CRC) 14167c478bd9Sstevel@tonic-gate return 1; 14177c478bd9Sstevel@tonic-gate 14187c478bd9Sstevel@tonic-gate /* 14197c478bd9Sstevel@tonic-gate * If we have a key for the encryption system, we assume it's 14207c478bd9Sstevel@tonic-gate * supported. 14217c478bd9Sstevel@tonic-gate */ 14227c478bd9Sstevel@tonic-gate return dbentry_has_key_for_enctype(context, client, enctype); 14237c478bd9Sstevel@tonic-gate } 14247c478bd9Sstevel@tonic-gate 14257c478bd9Sstevel@tonic-gate /* 14267c478bd9Sstevel@tonic-gate * This function returns the keytype which should be selected for the 14277c478bd9Sstevel@tonic-gate * session key. It is based on the ordered list which the user 14287c478bd9Sstevel@tonic-gate * requested, and what the KDC and the application server can support. 14297c478bd9Sstevel@tonic-gate */ 14307c478bd9Sstevel@tonic-gate krb5_enctype 143156a424ccSmp153739 select_session_keytype(krb5_context context, krb5_db_entry *server, 143256a424ccSmp153739 int nktypes, krb5_enctype *ktype) 14337c478bd9Sstevel@tonic-gate { 14347c478bd9Sstevel@tonic-gate int i; 14357c478bd9Sstevel@tonic-gate 14367c478bd9Sstevel@tonic-gate for (i = 0; i < nktypes; i++) { 1437505d05c7Sgtb if (!krb5_c_valid_enctype(ktype[i])) 14387c478bd9Sstevel@tonic-gate continue; 14397c478bd9Sstevel@tonic-gate 144056a424ccSmp153739 if (!krb5_is_permitted_enctype(context, ktype[i])) 144156a424ccSmp153739 continue; 144256a424ccSmp153739 14437c478bd9Sstevel@tonic-gate if (dbentry_supports_enctype(context, server, ktype[i])) 14447c478bd9Sstevel@tonic-gate return ktype[i]; 14457c478bd9Sstevel@tonic-gate } 14467c478bd9Sstevel@tonic-gate return 0; 14477c478bd9Sstevel@tonic-gate } 14487c478bd9Sstevel@tonic-gate 14497c478bd9Sstevel@tonic-gate /* 14507c478bd9Sstevel@tonic-gate * This function returns salt information for a particular client_key 14517c478bd9Sstevel@tonic-gate */ 14527c478bd9Sstevel@tonic-gate krb5_error_code 145356a424ccSmp153739 get_salt_from_key(krb5_context context, krb5_principal client, 145456a424ccSmp153739 krb5_key_data *client_key, krb5_data *salt) 14557c478bd9Sstevel@tonic-gate { 14567c478bd9Sstevel@tonic-gate krb5_error_code retval; 14577c478bd9Sstevel@tonic-gate krb5_data * realm; 14587c478bd9Sstevel@tonic-gate 14597c478bd9Sstevel@tonic-gate salt->data = 0; 146056a424ccSmp153739 salt->length = SALT_TYPE_NO_LENGTH; 14617c478bd9Sstevel@tonic-gate 14627c478bd9Sstevel@tonic-gate if (client_key->key_data_ver == 1) 14637c478bd9Sstevel@tonic-gate return 0; 14647c478bd9Sstevel@tonic-gate 14657c478bd9Sstevel@tonic-gate switch (client_key->key_data_type[1]) { 14667c478bd9Sstevel@tonic-gate case KRB5_KDB_SALTTYPE_NORMAL: 14677c478bd9Sstevel@tonic-gate break; 14687c478bd9Sstevel@tonic-gate case KRB5_KDB_SALTTYPE_V4: 14697c478bd9Sstevel@tonic-gate /* send an empty (V4) salt */ 14707c478bd9Sstevel@tonic-gate salt->data = 0; 14717c478bd9Sstevel@tonic-gate salt->length = 0; 14727c478bd9Sstevel@tonic-gate break; 14737c478bd9Sstevel@tonic-gate case KRB5_KDB_SALTTYPE_NOREALM: 14747c478bd9Sstevel@tonic-gate if ((retval = krb5_principal2salt_norealm(context, client, salt))) 14757c478bd9Sstevel@tonic-gate return retval; 14767c478bd9Sstevel@tonic-gate break; 14777c478bd9Sstevel@tonic-gate case KRB5_KDB_SALTTYPE_AFS3: 14787c478bd9Sstevel@tonic-gate /* send the same salt as with onlyrealm - but with no type info, 14797c478bd9Sstevel@tonic-gate we just hope they figure it out on the other end. */ 14807c478bd9Sstevel@tonic-gate /* fall through to onlyrealm: */ 14817c478bd9Sstevel@tonic-gate case KRB5_KDB_SALTTYPE_ONLYREALM: 14827c478bd9Sstevel@tonic-gate realm = krb5_princ_realm(context, client); 14837c478bd9Sstevel@tonic-gate salt->length = realm->length; 14847c478bd9Sstevel@tonic-gate if ((salt->data = malloc(realm->length)) == NULL) 14857c478bd9Sstevel@tonic-gate return ENOMEM; 14867c478bd9Sstevel@tonic-gate memcpy(salt->data, realm->data, realm->length); 14877c478bd9Sstevel@tonic-gate break; 14887c478bd9Sstevel@tonic-gate case KRB5_KDB_SALTTYPE_SPECIAL: 14897c478bd9Sstevel@tonic-gate salt->length = client_key->key_data_length[1]; 14907c478bd9Sstevel@tonic-gate if ((salt->data = malloc(salt->length)) == NULL) 14917c478bd9Sstevel@tonic-gate return ENOMEM; 14927c478bd9Sstevel@tonic-gate memcpy(salt->data, client_key->key_data_contents[1], salt->length); 14937c478bd9Sstevel@tonic-gate break; 14947c478bd9Sstevel@tonic-gate } 14957c478bd9Sstevel@tonic-gate return 0; 14967c478bd9Sstevel@tonic-gate } 14977c478bd9Sstevel@tonic-gate 14987c478bd9Sstevel@tonic-gate /* 14997c478bd9Sstevel@tonic-gate * Limit strings to a "reasonable" length to prevent crowding out of 15007c478bd9Sstevel@tonic-gate * other useful information in the log entry 15017c478bd9Sstevel@tonic-gate */ 15027c478bd9Sstevel@tonic-gate #define NAME_LENGTH_LIMIT 128 15037c478bd9Sstevel@tonic-gate 15047c478bd9Sstevel@tonic-gate void limit_string(char *name) 15057c478bd9Sstevel@tonic-gate { 15067c478bd9Sstevel@tonic-gate int i; 15077c478bd9Sstevel@tonic-gate 15087c478bd9Sstevel@tonic-gate if (!name) 15097c478bd9Sstevel@tonic-gate return; 15107c478bd9Sstevel@tonic-gate 15117c478bd9Sstevel@tonic-gate if (strlen(name) < NAME_LENGTH_LIMIT) 15127c478bd9Sstevel@tonic-gate return; 15137c478bd9Sstevel@tonic-gate 15147c478bd9Sstevel@tonic-gate i = NAME_LENGTH_LIMIT-4; 15157c478bd9Sstevel@tonic-gate name[i++] = '.'; 15167c478bd9Sstevel@tonic-gate name[i++] = '.'; 15177c478bd9Sstevel@tonic-gate name[i++] = '.'; 15187c478bd9Sstevel@tonic-gate name[i] = '\0'; 15197c478bd9Sstevel@tonic-gate return; 15207c478bd9Sstevel@tonic-gate } 152156a424ccSmp153739 152256a424ccSmp153739 /* 152356a424ccSmp153739 * L10_2 = log10(2**x), rounded up; log10(2) ~= 0.301. 152456a424ccSmp153739 */ 152556a424ccSmp153739 #define L10_2(x) ((int)(((x * 301) + 999) / 1000)) 152656a424ccSmp153739 152756a424ccSmp153739 /* 152856a424ccSmp153739 * Max length of sprintf("%ld") for an int of type T; includes leading 152956a424ccSmp153739 * minus sign and terminating NUL. 153056a424ccSmp153739 */ 153156a424ccSmp153739 #define D_LEN(t) (L10_2(sizeof(t) * CHAR_BIT) + 2) 153256a424ccSmp153739 153356a424ccSmp153739 void 153456a424ccSmp153739 ktypes2str(char *s, size_t len, int nktypes, krb5_enctype *ktype) 153556a424ccSmp153739 { 153656a424ccSmp153739 int i; 153756a424ccSmp153739 char stmp[D_LEN(krb5_enctype) + 1]; 153856a424ccSmp153739 char *p; 153956a424ccSmp153739 154056a424ccSmp153739 if (nktypes < 0 154156a424ccSmp153739 || len < (sizeof(" etypes {...}") + D_LEN(int))) { 154256a424ccSmp153739 *s = '\0'; 154356a424ccSmp153739 return; 154456a424ccSmp153739 } 154556a424ccSmp153739 154656a424ccSmp153739 sprintf(s, "%d etypes {", nktypes); 154756a424ccSmp153739 for (i = 0; i < nktypes; i++) { 154856a424ccSmp153739 sprintf(stmp, "%s%ld", i ? " " : "", (long)ktype[i]); 154956a424ccSmp153739 if (strlen(s) + strlen(stmp) + sizeof("}") > len) 155056a424ccSmp153739 break; 155156a424ccSmp153739 strcat(s, stmp); 155256a424ccSmp153739 } 155356a424ccSmp153739 if (i < nktypes) { 155456a424ccSmp153739 /* 155556a424ccSmp153739 * We broke out of the loop. Try to truncate the list. 155656a424ccSmp153739 */ 155756a424ccSmp153739 p = s + strlen(s); 155856a424ccSmp153739 while (p - s + sizeof("...}") > len) { 155956a424ccSmp153739 while (p > s && *p != ' ' && *p != '{') 156056a424ccSmp153739 *p-- = '\0'; 156156a424ccSmp153739 if (p > s && *p == ' ') { 156256a424ccSmp153739 *p-- = '\0'; 156356a424ccSmp153739 continue; 156456a424ccSmp153739 } 156556a424ccSmp153739 } 156656a424ccSmp153739 strcat(s, "..."); 156756a424ccSmp153739 } 156856a424ccSmp153739 strcat(s, "}"); 156956a424ccSmp153739 return; 157056a424ccSmp153739 } 157156a424ccSmp153739 157256a424ccSmp153739 void 157356a424ccSmp153739 rep_etypes2str(char *s, size_t len, krb5_kdc_rep *rep) 157456a424ccSmp153739 { 157556a424ccSmp153739 char stmp[sizeof("ses=") + D_LEN(krb5_enctype)]; 157656a424ccSmp153739 157756a424ccSmp153739 if (len < (3 * D_LEN(krb5_enctype) 157856a424ccSmp153739 + sizeof("etypes {rep= tkt= ses=}"))) { 157956a424ccSmp153739 *s = '\0'; 158056a424ccSmp153739 return; 158156a424ccSmp153739 } 158256a424ccSmp153739 158356a424ccSmp153739 sprintf(s, "etypes {rep=%ld", (long)rep->enc_part.enctype); 158456a424ccSmp153739 158556a424ccSmp153739 if (rep->ticket != NULL) { 158656a424ccSmp153739 sprintf(stmp, " tkt=%ld", (long)rep->ticket->enc_part.enctype); 158756a424ccSmp153739 strcat(s, stmp); 158856a424ccSmp153739 } 158956a424ccSmp153739 159056a424ccSmp153739 if (rep->ticket != NULL 159156a424ccSmp153739 && rep->ticket->enc_part2 != NULL 159256a424ccSmp153739 && rep->ticket->enc_part2->session != NULL) { 159356a424ccSmp153739 sprintf(stmp, " ses=%ld", 159456a424ccSmp153739 (long)rep->ticket->enc_part2->session->enctype); 159556a424ccSmp153739 strcat(s, stmp); 159656a424ccSmp153739 } 159756a424ccSmp153739 strcat(s, "}"); 159856a424ccSmp153739 return; 159956a424ccSmp153739 } 1600