get_in_tkt.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
*
* Copyright 1990,1991, 2003 by the Massachusetts Institute of Technology.
* All Rights Reserved.
*
* Export of this software from the United States of America may
* require a specific license from the United States Government.
* It is the responsibility of any person or organization contemplating
* export to obtain such a license before exporting.
*
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
* distribute this software and its documentation for any purpose and
* without fee is hereby granted, provided that the above copyright
* notice appear in all copies and that both that copyright notice and
* this permission notice appear in supporting documentation, and that
* the name of M.I.T. not be used in advertising or publicity pertaining
* to distribution of the software without specific, written prior
* permission. Furthermore if you modify this software you must label
* your software as modified software and not distribute it in such a
* fashion that it might be confused with the original M.I.T. software.
* M.I.T. makes no representations about the suitability of
* this software for any purpose. It is provided "as is" without express
* or implied warranty.
*
*
* krb5_get_in_tkt()
*/
#include <string.h>
#include <k5-int.h>
/*
All-purpose initial ticket routine, usually called via
krb5_get_in_tkt_with_password or krb5_get_in_tkt_with_skey.
Attempts to get an initial ticket for creds->client to use server
creds->server, (realm is taken from creds->client), with options
options, and using creds->times.starttime, creds->times.endtime,
creds->times.renew_till as from, till, and rtime.
creds->times.renew_till is ignored unless the RENEWABLE option is requested.
key_proc is called to fill in the key to be used for decryption.
keyseed is passed on to key_proc.
decrypt_proc is called to perform the decryption of the response (the
encrypted part is in dec_rep->enc_part; the decrypted part should be
allocated and filled into dec_rep->enc_part2
arg is passed on to decrypt_proc.
If addrs is non-NULL, it is used for the addresses requested. If it is
null, the system standard addresses are used.
A succesful call will place the ticket in the credentials cache ccache
returns system errors, encryption errors
*/
/* some typedef's for the function args to make things look a bit cleaner */
const krb5_enctype,
krb5_data *,
krb5_keyblock **));
const krb5_keyblock *,
krb5_kdc_rep * ));
int, krb5_pa_data ***));
/*
* This function sends a request to the KDC, and gets back a response;
* the response is parsed into ret_err_reply or ret_as_reply if the
* reponse is a KRB_ERROR or a KRB_AS_REP packet. If it is some other
* unexpected response, an error is returned.
*/
static krb5_error_code
int use_master;
{
krb5_kdc_rep *as_reply = 0;
char k4_version; /* same type as *(krb5_data::data) */
int tcp_only = 0;
goto cleanup;
/*
* XXX we know they are the same size... and we should do
* something better than just the current time
*/
/* encode & send to KDC */
goto cleanup;
if (retval)
goto cleanup;
/* now decode the reply...could be error or as_rep */
if (krb5_is_krb_error(&reply)) {
/* some other error code--??? */
goto cleanup;
if (ret_err_reply) {
&& tcp_only == 0) {
tcp_only = 1;
goto send_again;
}
} else
goto cleanup;
}
/*
* Check to make sure it isn't a V4 reply.
*/
if (!krb5_is_as_rep(&reply)) {
/* these are in <kerberosIV/prot.h> as well but it isn't worth including. */
#define V4_KRB_PROT_VERSION 4
/* check here for V4 reply */
unsigned int t_switch;
/* From v4 g_in_tkt.c: This used to be
switch (pkt_msg_type(rpkt) & ~1) {
but SCO 3.2v4 cc compiled that incorrectly. */
t_switch &= ~1;
if (t_switch == V4_AUTH_MSG_ERR_REPLY
} else {
}
goto cleanup;
}
/* It must be a KRB_AS_REP message, or an bad returned packet */
/* some other error code ??? */
goto cleanup;
goto cleanup;
}
if (ret_as_reply)
*ret_as_reply = as_reply;
else
if (packet)
return retval;
}
static krb5_error_code
krb5_keyblock * key;
{
krb5_keyblock * decrypt_key = 0;
return 0;
if (key)
decrypt_key = key;
return(retval);
if (retval)
goto cleanup;
} else {
"error key == NULL and request == NULL");
return (EINVAL);
}
/* Solaris kerberos: Overwriting the decrypt_key->enctype because the
* decrypt key's enctype may not be an exact match with the enctype that the
* KDC used to encrypt this part of the AS reply. This assumes the
* as_reply->enc_part.enctype has been validated which is done by checking
* to see if the enctype that the KDC sent back in the as_reply is one of
* the enctypes originally requested. Note, if request is NULL then the
* as_reply->enc_part.enctype could not be validated.
*/
} else {
"error is_in_keytype() returned false");
goto cleanup;
}
}
"= %d", retval);
goto cleanup;
}
if (!key && decrypt_key)
return (retval);
}
static krb5_error_code
{
/* check the contents for sanity: */
/* XXX check for extraneous flags */
/* XXX || (!krb5_addresses_compare(context, addrs, as_reply->enc_part2->caddrs)) */
)
return KRB5_KDCREP_MODIFIED;
if (retval)
return retval;
} else {
return (KRB5_KDCREP_SKEW);
}
return 0;
}
/*ARGSUSED*/
static krb5_error_code
krb5_creds * creds;
{
goto cleanup;
&server))
goto cleanup;
/* fill in the credentials */
goto cleanup;
be encrypted in skey */
goto cleanup;
goto cleanup;
/* store it in the ccache! */
if (ccache)
goto cleanup;
if (retval) {
if (client)
if (server)
}
}
}
}
return (retval);
}
/*ARGSUSED*/
static krb5_error_code
int nptypes;
krb5_pa_data *** ret_list;
{
krb5_pa_data ** preauthp;
int i;
if (nptypes < 0) {
;
}
/* allocate space for a NULL to terminate the list */
if ((preauthp =
return(ENOMEM);
for (i=0; i<nptypes; i++) {
if ((preauthp[i] =
for (; i>=0; i++)
return (ENOMEM);
}
}
/* fill in the terminating NULL */
return 0;
}
#define MAX_IN_TKT_LOOPS 16
/* begin libdefaults parsing code. This should almost certainly move
somewhere else, but I don't know where the correct somewhere else
is yet. */
/* XXX Duplicating this is annoying; try to work on a better way.*/
static char *conf_yes[] = {
"y", "yes", "true", "t", "1", "on",
0,
};
static char *conf_no[] = {
"n", "no", "false", "nil", "0", "off",
0,
};
int
char *s;
{
char **p;
for(p=conf_yes; *p; p++) {
if (!strcasecmp(*p,s))
return 1;
}
for(p=conf_no; *p; p++) {
if (!strcasecmp(*p,s))
return 0;
}
/* Default to "no" */
return 0;
}
static krb5_error_code
const char *option;
char **ret_value;
{
const char *names[5];
char realmstr[1024];
return(EINVAL);
return KV5M_CONTEXT;
names[0] = "realms";
/*
* Try number one:
*
* [realms]
* REALM = {
* option = <boolean>
* }
*/
names[3] = 0;
goto goodbye;
/*
* Try number two:
*
* [libdefaults]
* option = <boolean>
*/
names[0] = "libdefaults";
names[2] = 0;
goto goodbye;
if (!nameval)
return(ENOENT);
if (!nameval[0]) {
} else {
if (!*ret_value)
else
}
return retval;
}
/* not static so verify_init_creds() can call it */
/* as well as the DNS code */
const char *option;
int *ret_value;
{
if (retval)
return(retval);
return(0);
}
void *prompter_data;
char *in_tkt_service;
void *gak_data;
int use_master;
{
int tempint;
char *tempstr;
int loopcount;
krb5_enctype etype = 0;
/* initialize everything which will be freed at cleanup */
local_as_reply = 0;
/*
* Set up the basic request structure
*/
/* request.padata is filled in later */
/* forwardable */
"forwardable", &tempint)) == 0)
/*EMPTY*/
;
else
tempint = 0;
if (tempint)
/* proxiable */
"proxiable", &tempint)) == 0)
/*EMPTY*/
;
else
tempint = 0;
if (tempint)
/* renewable */
"renew_lifetime", &tempstr))
== 0) {
goto cleanup;
}
} else {
renew_life = 0;
}
if (renew_life > 0)
/* allow_postdate */
if (start_time > 0)
/* client */
if (in_tkt_service) {
/* this is ugly, because so are the data structures involved. I'm
in the library, so I'm going to manipulate the data structures
directly, otherwise, it will be worse. */
goto cleanup;
/* stuff the client realm into the server principal.
realloc if necessary */
goto cleanup;
}
} else {
0))
goto cleanup;
}
goto cleanup;
"max_lifetime", &tempstr)) == 0) {
/* Solaris Kerberos: max_lifetime parameter support (tkt lifetime) */
goto cleanup;
}
} else {
/* Solaris Kerberos: defaulting to infinity. Note 0 == infinity (ASN1
* encoding will do the right thing).
*/
}
if (renew_life > 0) {
} else {
}
/* nonce is filled in by send_as_request */
;
} else {
/* there isn't any useful default here. ret is set from above */
goto cleanup;
}
}
/* it would be nice if this parsed out an address list, but
that would be work. */
"no_addresses", &tempint)) == 0)
&& tempint) {
/*EMPTY*/
;
"noaddresses", &tempint)) == 0)
&& tempint) {
/*EMPTY*/
;
} else {
goto cleanup;
}
request.unenc_authdata = 0;
request.second_ticket = 0;
/* set up the other state. */
&padata))
goto cleanup;
}
/* the salt is allocated from somewhere, unless it is from the caller,
then it is a reference */
} else {
}
/* now, loop processing preauth data and talking to the kdc */
}
goto cleanup;
if (padata) {
padata = 0;
}
err_reply = 0;
local_as_reply = 0;
&local_as_reply, use_master)))
goto cleanup;
if (err_reply) {
&padata);
if (ret)
goto cleanup;
} else {
goto cleanup;
}
} else if (local_as_reply) {
break;
} else {
goto cleanup;
}
}
if (loopcount == MAX_IN_TKT_LOOPS) {
goto cleanup;
}
/* process any preauth data in the as_reply */
goto cleanup;
/* XXX if there's padata on output, something is wrong, but it's
not obviously an error */
/* XXX For 1.1.1 and prior KDC's, when SAM is used w/ USE_SAD_AS_KEY,
the AS_REP comes back encrypted in the user's longterm key
instead of in the SAD. If there was a SAM preauth, there
will be an as_key here which will be the SAD. If that fails,
use the gak_fct to get the password, and try again. */
/* XXX because etypes are handled poorly (particularly wrt SAM,
where the etype is fixed by the kdc), we may want to try
decrypt_as_reply twice. If there's an as_key available, try
it. If decrypting the as_rep fails, or if there isn't an
as_key at all yet, then use the gak_fct to get one, and try
again. */
else
ret = -1;
if (ret) {
/* if we haven't get gotten a key, get it now */
goto cleanup;
goto cleanup;
}
goto cleanup;
/*
* XXX this should be inside stash_as_reply, but as long as
* do that
*/
goto cleanup;
/* success */
ret = 0;
(!(options &&
if (padata)
if (as_reply)
else if (local_as_reply)
return(ret);
}