/* $OpenBSD: rd_req.c,v 1.5 1997/12/09 07:57:34 art Exp $ */ /* $KTH: rd_req.c,v 1.24 1997/05/11 11:05:28 assar Exp $ */ /* * Copyright (c) 1995, 1996, 1997 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the Kungliga Tekniska * Högskolan and its contributors. * * 4. Neither the name of the Institute nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "krb_locl.h" static struct timeval t_local = { 0, 0 }; /* * Keep the following information around for subsequent calls * to this routine by the same server using the same key. */ static des_key_schedule serv_key; /* Key sched to decrypt ticket */ static des_cblock ky; /* Initialization vector */ static int st_kvno; /* version number for this key */ static char st_rlm[REALM_SZ]; /* server's realm */ static char st_nam[ANAME_SZ]; /* service name */ static char st_inst[INST_SZ]; /* server's instance */ /* * This file contains two functions. krb_set_key() takes a DES * key or password string and returns a DES key (either the original * key, or the password converted into a DES key) and a key schedule * for it. * * krb_rd_req() reads an authentication request and returns information * about the identity of the requestor, or an indication that the * identity information was not authentic. */ /* * krb_set_key() takes as its first argument either a DES key or a * password string. The "cvt" argument indicates how the first * argument "key" is to be interpreted: if "cvt" is null, "key" is * taken to be a DES key; if "cvt" is non-null, "key" is taken to * be a password string, and is converted into a DES key using * string_to_key(). In either case, the resulting key is returned * in the external static variable "ky". A key schedule is * generated for "ky" and returned in the external static variable * "serv_key". * * This routine returns the return value of des_key_sched. * * krb_set_key() needs to be in the same .o file as krb_rd_req() so that * the key set by krb_set_key() is available in private storage for * krb_rd_req(). */ int krb_set_key(void *key, int cvt) { #ifdef NOENCRYPTION memset(ky, 0, sizeof(ky)); return KSUCCESS; #else /* Encrypt */ if (cvt) des_string_to_key((char*)key, &ky); else memcpy((char*)ky, key, 8); return(des_key_sched(&ky, serv_key)); #endif /* NOENCRYPTION */ } /* * krb_rd_req() takes an AUTH_MSG_APPL_REQUEST or * AUTH_MSG_APPL_REQUEST_MUTUAL message created by krb_mk_req(), * checks its integrity and returns a judgement as to the requestor's * identity. * * The "authent" argument is a pointer to the received message. * The "service" and "instance" arguments name the receiving server, * and are used to get the service's ticket to decrypt the ticket * in the message, and to compare against the server name inside the * ticket. "from_addr" is the network address of the host from which * the message was received; this is checked against the network * address in the ticket. If "from_addr" is zero, the check is not * performed. "ad" is an AUTH_DAT structure which is * filled in with information about the sender's identity according * to the authenticator and ticket sent in the message. Finally, * "fn" contains the name of the file containing the server's key. * (If "fn" is NULL, the server's key is assumed to have been set * by krb_set_key(). If "fn" is the null string ("") the default * file KEYFILE, defined in "krb.h", is used.) * * krb_rd_req() returns RD_AP_OK if the authentication information * was genuine, or one of the following error codes (defined in * "krb.h"): * * RD_AP_VERSION - wrong protocol version number * RD_AP_MSG_TYPE - wrong message type * RD_AP_UNDEC - couldn't decipher the message * RD_AP_INCON - inconsistencies found * RD_AP_BADD - wrong network address * RD_AP_TIME - client time (in authenticator) * too far off server time * RD_AP_NYV - Kerberos time (in ticket) too * far off server time * RD_AP_EXP - ticket expired * * For the message format, see krb_mk_req(). * * Mutual authentication is not implemented. */ int krb_rd_req(KTEXT authent, /* The received message */ char *service, /* Service name */ char *instance, /* Service instance */ int32_t from_addr, /* Net address of originating host */ AUTH_DAT *ad, /* Structure to be filled in */ char *fn) /* Filename to get keys from */ { static KTEXT_ST ticket; /* Temp storage for ticket */ static KTEXT tkt = &ticket; static KTEXT_ST req_id_st; /* Temp storage for authenticator */ KTEXT req_id = &req_id_st; char realm[REALM_SZ]; /* Realm of issuing kerberos */ unsigned char skey[KKEY_SZ]; /* Session key from ticket */ char sname[SNAME_SZ]; /* Service name from ticket */ char iname[INST_SZ]; /* Instance name from ticket */ char r_aname[ANAME_SZ]; /* Client name from authenticator */ char r_inst[INST_SZ]; /* Client instance from authenticator */ char r_realm[REALM_SZ]; /* Client realm from authenticator */ u_int32_t r_time_sec; /* Coarse time from authenticator */ unsigned long delta_t; /* Time in authenticator - local time */ long tkt_age; /* Age of ticket */ static unsigned char s_kvno;/* Version number of the server's key * Kerberos used to encrypt ticket */ struct timeval tv; int status; int pvno; int type; int little_endian; unsigned char *p; if (authent->length <= 0) return(RD_AP_MODIFIED); p = authent->dat; /* get msg version, type and byte order, and server key version */ pvno = *p++; if(pvno != KRB_PROT_VERSION) return RD_AP_VERSION; type = *p++; little_endian = type & 1; type &= ~1; if(type != AUTH_MSG_APPL_REQUEST && type != AUTH_MSG_APPL_REQUEST_MUTUAL) return RD_AP_MSG_TYPE; s_kvno = *p++; p += krb_get_string(p, realm); /* * If "fn" is NULL, key info should already be set; don't * bother with ticket file. Otherwise, check to see if we * already have key info for the given server and key version * (saved in the static st_* variables). If not, go get it * from the ticket file. If "fn" is the null string, use the * default ticket file. */ if (fn && (strcmp(st_nam,service) || strcmp(st_inst,instance) || strcmp(st_rlm,realm) || (st_kvno != s_kvno))) { if (*fn == 0) fn = KEYFILE; st_kvno = s_kvno; if (read_service_key(service, instance, realm, s_kvno, fn, (char *)skey)) return(RD_AP_UNDEC); if ((status = krb_set_key((char*)skey, 0))) return(status); strncpy(st_rlm, realm, REALM_SZ); st_rlm[REALM_SZ-1] = '\0'; strncpy(st_nam, service, SNAME_SZ); st_nam[SNAME_SZ-1] = '\0'; strncpy(st_inst, instance, INST_SZ); st_inst[INST_SZ-1] = '\0'; } tkt->length = *p++; req_id->length = *p++; if(tkt->length + (p - authent->dat) > authent->length) return RD_AP_MODIFIED; memcpy(tkt->dat, p, tkt->length); p += tkt->length; if (krb_ap_req_debug) krb_log("ticket->length: %d",tkt->length); /* Decrypt and take apart ticket */ if (decomp_ticket(tkt, &ad->k_flags, ad->pname, ad->pinst, ad->prealm, &ad->address, ad->session, &ad->life, &ad->time_sec, sname, iname, &ky, serv_key)) return RD_AP_UNDEC; if (krb_ap_req_debug) { krb_log("Ticket Contents."); krb_log(" Aname: %s.%s",ad->pname, ad->prealm); krb_log(" Service: %s", krb_unparse_name_long(sname, iname, NULL)); } /* Extract the authenticator */ if(req_id->length + (p - authent->dat) > authent->length) return RD_AP_MODIFIED; memcpy(req_id->dat, p, req_id->length); p = req_id->dat; #ifndef NOENCRYPTION /* And decrypt it with the session key from the ticket */ if (krb_ap_req_debug) krb_log("About to decrypt authenticator"); encrypt_ktext(req_id, &ad->session, DES_DECRYPT); if (krb_ap_req_debug) krb_log("Done."); #endif /* NOENCRYPTION */ /* cast req_id->length to int? */ #define check_ptr() if ((ptr - (char *) req_id->dat) > req_id->length) return(RD_AP_MODIFIED); p += krb_get_nir(p, r_aname, r_inst, r_realm); /* XXX no rangecheck */ p += krb_get_int(p, &ad->checksum, 4, little_endian); p++; /* time_5ms is not used */ p += krb_get_int(p, &r_time_sec, 4, little_endian); /* Check for authenticity of the request */ if (krb_ap_req_debug) krb_log("Principal: %s.%s@%s / %s.%s@%s",ad->pname,ad->pinst, ad->prealm, r_aname, r_inst, r_realm); if (strcmp(ad->pname, r_aname) != 0 || strcmp(ad->pinst, r_inst) != 0 || strcmp(ad->prealm, r_realm) != 0) return RD_AP_INCON; if (krb_ap_req_debug) krb_log("Address: %x %x", ad->address, from_addr); if (from_addr && (!krb_equiv(ad->address, from_addr))) return RD_AP_BADD; gettimeofday(&tv, NULL); delta_t = abs((int)(tv.tv_sec - r_time_sec)); if (delta_t > CLOCK_SKEW) { if (krb_ap_req_debug) krb_log("Time out of range: %lu - %lu = %lu", (unsigned long)t_local.tv_sec, (unsigned long)r_time_sec, (unsigned long)delta_t); return RD_AP_TIME; } /* Now check for expiration of ticket */ tkt_age = tv.tv_sec - ad->time_sec; if (krb_ap_req_debug) krb_log("Time: %ld Issue Date: %lu Diff: %ld Life %x", (long)tv.tv_sec, (unsigned long)ad->time_sec, tkt_age, ad->life); if ((tkt_age < 0) && (-tkt_age > CLOCK_SKEW)) return RD_AP_NYV; if (tv.tv_sec > krb_life_to_time(ad->time_sec, ad->life)) return RD_AP_EXP; /* All seems OK */ ad->reply.length = 0; return(RD_AP_OK); }