/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */1/* kdc/kdc_transit.c */2/*3* Copyright (C) 2012 by the Massachusetts Institute of Technology.4* All rights reserved.5*6* Redistribution and use in source and binary forms, with or without7* modification, are permitted provided that the following conditions8* are met:9*10* * Redistributions of source code must retain the above copyright11* notice, this list of conditions and the following disclaimer.12*13* * Redistributions in binary form must reproduce the above copyright14* notice, this list of conditions and the following disclaimer in15* the documentation and/or other materials provided with the16* distribution.17*18* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS19* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT20* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS21* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE22* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,23* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES24* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR25* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)26* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,27* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)28* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED29* OF THE POSSIBILITY OF SUCH DAMAGE.30*/3132#include "k5-int.h"33#include "kdc_util.h"3435#define MAX_REALM_LN 5003637/*38* subrealm - determine if r2 is a subrealm of r139*40* SUBREALM takes two realms, r1 and r2, and41* determines if r2 is a subrealm of r1.42* r2 is a subrealm of r1 if (r1 is a prefix43* of r2 AND r1 and r2 begin with a /) or if44* (r1 is a suffix of r2 and neither r1 nor r245* begin with a /).46*47* RETURNS: If r2 is a subrealm, and r1 is a prefix, the number48* of characters in the suffix of r2 is returned as a49* negative number.50*51* If r2 is a subrealm, and r1 is a suffix, the number52* of characters in the prefix of r2 is returned as a53* positive number.54*55* If r2 is not a subrealm, SUBREALM returns 0.56*/57static int58subrealm(char *r1, char *r2)59{60size_t l1,l2;61l1 = strlen(r1);62l2 = strlen(r2);63if(l2 <= l1) return(0);64if((*r1 == '/') && (*r2 == '/') && (strncmp(r1,r2,l1) == 0)) return(l1-l2);65if((*r1 != '/') && (*r2 != '/') && (strncmp(r1,r2+l2-l1,l1) == 0))66return(l2-l1);67return(0);68}6970/*71* add_to_transited Adds the name of the realm which issued the72* ticket granting ticket on which the new ticket to73* be issued is based (note that this is the same as74* the realm of the server listed in the ticket75* granting ticket.76*77* ASSUMPTIONS: This procedure assumes that the transited field from78* the existing ticket granting ticket already appears79* in compressed form. It will add the new realm while80* maintaining that form. As long as each successive81* realm is added using this (or a similar) routine, the82* transited field will be in compressed form. The83* basis step is an empty transited field which is, by84* its nature, in its most compressed form.85*86* ARGUMENTS: krb5_data *tgt_trans Transited field from TGT87* krb5_data *new_trans The transited field for the new ticket88* krb5_principal tgs Name of ticket granting server89* This includes the realm of the KDC90* that issued the ticket granting91* ticket. This is the realm that is92* to be added to the transited field.93* krb5_principal client Name of the client94* krb5_principal server The name of the requested server.95* This may be the an intermediate96* ticket granting server.97*98* The last two argument are needed since they are99* implicitly part of the transited field of the new ticket100* even though they are not explicitly listed.101*102* RETURNS: krb5_error_code - Success, or out of memory103*104* MODIFIES: new_trans: ->length will contain the length of the new105* transited field.106*107* If ->data was not null when this procedure108* is called, the memory referenced by ->data109* will be deallocated.110*111* Memory will be allocated for the new transited field112* ->data will be updated to point to the newly113* allocated memory.114*115* BUGS: The space allocated for the new transited field is the116* maximum that might be needed given the old transited field,117* and the realm to be added. This length is calculated118* assuming that no compression of the new realm is possible.119* This has no adverse consequences other than the allocation120* of more space than required.121*122* This procedure will not yet use the null subfield notation,123* and it will get confused if it sees it.124*125* This procedure does not check for quoted commas in realm126* names.127*/128129char *130data2string (krb5_data *d)131{132char *s;133s = malloc(d->length + 1);134if (s) {135if (d->length > 0)136memcpy(s, d->data, d->length);137s[d->length] = 0;138}139return s;140}141142krb5_error_code143add_to_transited(krb5_data *tgt_trans, krb5_data *new_trans,144krb5_principal tgs, krb5_principal client,145krb5_principal server)146{147krb5_error_code retval;148char *realm;149char *trans;150char *otrans, *otrans_ptr;151size_t bufsize;152153/* The following are for stepping through the transited field */154155char prev[MAX_REALM_LN];156char next[MAX_REALM_LN];157char current[MAX_REALM_LN];158char exp[MAX_REALM_LN]; /* Expanded current realm name */159160int i;161int clst, nlst; /* count of last character in current and next */162int pl, pl1; /* prefix length */163int added; /* TRUE = new realm has been added */164165realm = data2string(krb5_princ_realm(kdc_context, tgs));166if (realm == NULL)167return(ENOMEM);168169otrans = data2string(tgt_trans);170if (otrans == NULL) {171free(realm);172return(ENOMEM);173}174/* Keep track of start so we can free */175otrans_ptr = otrans;176177/* +1 for null,178+1 for extra comma which may be added between179+1 for potential space when leading slash in realm */180bufsize = strlen(realm) + strlen(otrans) + 3;181if (bufsize > MAX_REALM_LN)182bufsize = MAX_REALM_LN;183if (!(trans = (char *) malloc(bufsize))) {184retval = ENOMEM;185goto fail;186}187188if (new_trans->data) free(new_trans->data);189new_trans->data = trans;190new_trans->length = 0;191192trans[0] = '\0';193194/* For the purpose of appending, the realm preceding the first */195/* realm in the transited field is considered the null realm */196197prev[0] = '\0';198199/* read field into current */200for (i = 0; *otrans != '\0';) {201if (*otrans == '\\') {202if (*(++otrans) == '\0')203break;204else205continue;206}207if (*otrans == ',') {208otrans++;209break;210}211current[i++] = *otrans++;212if (i >= MAX_REALM_LN) {213retval = KRB5KRB_AP_ERR_ILL_CR_TKT;214goto fail;215}216}217current[i] = '\0';218219added = (krb5_princ_realm(kdc_context, client)->length == strlen(realm) &&220!strncmp(krb5_princ_realm(kdc_context, client)->data, realm, strlen(realm))) ||221(krb5_princ_realm(kdc_context, server)->length == strlen(realm) &&222!strncmp(krb5_princ_realm(kdc_context, server)->data, realm, strlen(realm)));223224while (current[0]) {225226/* figure out expanded form of current name */227228clst = strlen(current) - 1;229if (current[0] == ' ') {230strncpy(exp, current+1, sizeof(exp) - 1);231exp[sizeof(exp) - 1] = '\0';232}233else if ((current[0] == '/') && (prev[0] == '/')) {234strncpy(exp, prev, sizeof(exp) - 1);235exp[sizeof(exp) - 1] = '\0';236if (strlen(exp) + strlen(current) + 1 >= MAX_REALM_LN) {237retval = KRB5KRB_AP_ERR_ILL_CR_TKT;238goto fail;239}240strncat(exp, current, sizeof(exp) - 1 - strlen(exp));241}242else if (current[clst] == '.') {243strncpy(exp, current, sizeof(exp) - 1);244exp[sizeof(exp) - 1] = '\0';245if (strlen(exp) + strlen(prev) + 1 >= MAX_REALM_LN) {246retval = KRB5KRB_AP_ERR_ILL_CR_TKT;247goto fail;248}249strncat(exp, prev, sizeof(exp) - 1 - strlen(exp));250}251else {252strncpy(exp, current, sizeof(exp) - 1);253exp[sizeof(exp) - 1] = '\0';254}255256/* read field into next */257for (i = 0; *otrans != '\0';) {258if (*otrans == '\\') {259if (*(++otrans) == '\0')260break;261else262continue;263}264if (*otrans == ',') {265otrans++;266break;267}268next[i++] = *otrans++;269if (i >= MAX_REALM_LN) {270retval = KRB5KRB_AP_ERR_ILL_CR_TKT;271goto fail;272}273}274next[i] = '\0';275nlst = i - 1;276277if (!strcmp(exp, realm)) added = TRUE;278279/* If we still have to insert the new realm */280281if (!added) {282283/* Is the next field compressed? If not, and if the new */284/* realm is a subrealm of the current realm, compress */285/* the new realm, and insert immediately following the */286/* current one. Note that we can not do this if the next*/287/* field is already compressed since it would mess up */288/* what has already been done. In most cases, this is */289/* not a problem because the realm to be added will be a */290/* subrealm of the next field too, and we will catch */291/* it in a future iteration. */292293/* Note that the second test here is an unsigned comparison,294so the first half (or a cast) is also required. */295assert(nlst < 0 || nlst < (int)sizeof(next));296if ((nlst < 0 || next[nlst] != '.') &&297(next[0] != '/') &&298(pl = subrealm(exp, realm))) {299added = TRUE;300current[sizeof(current) - 1] = '\0';301if (strlen(current) + (pl>0?pl:-pl) + 2 >= MAX_REALM_LN) {302retval = KRB5KRB_AP_ERR_ILL_CR_TKT;303goto fail;304}305strncat(current, ",", sizeof(current) - 1 - strlen(current));306if (pl > 0) {307strncat(current, realm, (unsigned) pl);308}309else {310strncat(current, realm+strlen(realm)+pl, (unsigned) (-pl));311}312}313314/* Whether or not the next field is compressed, if the */315/* realm to be added is a superrealm of the current realm,*/316/* then the current realm can be compressed. First the */317/* realm to be added must be compressed relative to the */318/* previous realm (if possible), and then the current */319/* realm compressed relative to the new realm. Note that */320/* if the realm to be added is also a superrealm of the */321/* previous realm, it would have been added earlier, and */322/* we would not reach this step this time around. */323324else if ((pl = subrealm(realm, exp))) {325added = TRUE;326current[0] = '\0';327if ((pl1 = subrealm(prev,realm))) {328if (strlen(current) + (pl1>0?pl1:-pl1) + 1 >= MAX_REALM_LN) {329retval = KRB5KRB_AP_ERR_ILL_CR_TKT;330goto fail;331}332if (pl1 > 0) {333strncat(current, realm, (unsigned) pl1);334}335else {336strncat(current, realm+strlen(realm)+pl1, (unsigned) (-pl1));337}338}339else { /* If not a subrealm */340if ((realm[0] == '/') && prev[0]) {341if (strlen(current) + 2 >= MAX_REALM_LN) {342retval = KRB5KRB_AP_ERR_ILL_CR_TKT;343goto fail;344}345strncat(current, " ", sizeof(current) - 1 - strlen(current));346current[sizeof(current) - 1] = '\0';347}348if (strlen(current) + strlen(realm) + 1 >= MAX_REALM_LN) {349retval = KRB5KRB_AP_ERR_ILL_CR_TKT;350goto fail;351}352strncat(current, realm, sizeof(current) - 1 - strlen(current));353current[sizeof(current) - 1] = '\0';354}355if (strlen(current) + (pl>0?pl:-pl) + 2 >= MAX_REALM_LN) {356retval = KRB5KRB_AP_ERR_ILL_CR_TKT;357goto fail;358}359strncat(current,",", sizeof(current) - 1 - strlen(current));360current[sizeof(current) - 1] = '\0';361if (pl > 0) {362strncat(current, exp, (unsigned) pl);363}364else {365strncat(current, exp+strlen(exp)+pl, (unsigned)(-pl));366}367}368}369370if (new_trans->length != 0) {371if (strlcat(trans, ",", bufsize) >= bufsize) {372retval = KRB5KRB_AP_ERR_ILL_CR_TKT;373goto fail;374}375}376if (strlcat(trans, current, bufsize) >= bufsize) {377retval = KRB5KRB_AP_ERR_ILL_CR_TKT;378goto fail;379}380new_trans->length = strlen(trans);381382strncpy(prev, exp, sizeof(prev) - 1);383prev[sizeof(prev) - 1] = '\0';384strncpy(current, next, sizeof(current) - 1);385current[sizeof(current) - 1] = '\0';386}387388if (!added) {389if (new_trans->length != 0) {390if (strlcat(trans, ",", bufsize) >= bufsize) {391retval = KRB5KRB_AP_ERR_ILL_CR_TKT;392goto fail;393}394}395if((realm[0] == '/') && trans[0]) {396if (strlcat(trans, " ", bufsize) >= bufsize) {397retval = KRB5KRB_AP_ERR_ILL_CR_TKT;398goto fail;399}400}401if (strlcat(trans, realm, bufsize) >= bufsize) {402retval = KRB5KRB_AP_ERR_ILL_CR_TKT;403goto fail;404}405new_trans->length = strlen(trans);406}407408retval = 0;409fail:410free(realm);411free(otrans_ptr);412return (retval);413}414415416