#include "ksu.h"
static krb5_error_code krb5_verify_tkt_def
(krb5_context,
krb5_principal,
krb5_principal,
krb5_keyblock *,
krb5_data *,
krb5_ticket **);
void plain_dump_principal ();
krb5_preauthtype * preauth_ptr = NULL;
krb5_boolean krb5_auth_check(context, client_pname, hostname, options,
target_user, cc, path_passwd, target_uid)
krb5_context context;
krb5_principal client_pname;
char *hostname;
opt_info *options;
char *target_user;
uid_t target_uid;
krb5_ccache cc;
int *path_passwd;
{
krb5_principal client, server;
krb5_creds tgt, tgtq, in_creds, * out_creds;
krb5_creds **tgts = NULL;
krb5_ticket * target_tkt;
krb5_error_code retval =0;
int got_it = 0;
krb5_boolean zero_password;
*path_passwd = 0;
memset((char *) &tgtq, 0, sizeof(tgtq));
memset((char *) &tgt, 0, sizeof(tgt));
memset((char *) &in_creds, 0, sizeof(krb5_creds));
if ((retval= krb5_copy_principal(context, client_pname, &client))){
com_err(prog_name, retval,"while copying client principal");
return (FALSE) ;
}
if (auth_debug) {
dump_principal(context, "krb5_auth_check: Client principal name",
client);
}
if ((retval = krb5_sname_to_principal(context, hostname, NULL,
KRB5_NT_SRV_HST, &server))){
com_err(prog_name, retval,
"while creating server %s principal name", hostname);
krb5_free_principal(context, client);
return (FALSE) ;
}
if (auth_debug) {
dump_principal(context, "krb5_auth_check: Server principal name",
server);
}
if( krb5_fast_auth(context, client, server, target_user, cc) == TRUE){
if (auth_debug ){
fprintf (stderr,"Authenticated via fast_auth \n");
}
return TRUE;
}
if ((retval= krb5_copy_principal(context, client, &tgtq.client))){
com_err(prog_name, retval,"while copying client principal");
return (FALSE) ;
}
if ((retval = ksu_tgtname(context, krb5_princ_realm(context, client),
krb5_princ_realm(context, client),
&tgtq.server))){
com_err(prog_name, retval, "while creating tgt for local realm");
krb5_free_principal(context, client);
krb5_free_principal(context, server);
return (FALSE) ;
}
if (auth_debug){ dump_principal(context, "local tgt principal name", tgtq.server ); }
retval = krb5_cc_retrieve_cred(context, cc,
KRB5_TC_MATCH_SRV_NAMEONLY | KRB5_TC_SUPPORTED_KTYPES,
&tgtq, &tgt);
if (! retval) retval = krb5_check_exp(context, tgt.times);
if (retval){
if ((retval != KRB5_CC_NOTFOUND) &&
(retval != KRB5KRB_AP_ERR_TKT_EXPIRED)){
com_err(prog_name, retval,
"while retrieving creds from cache");
return (FALSE) ;
}
} else{
got_it = 1;
}
if (! got_it){
#ifdef GET_TGT_VIA_PASSWD
if (krb5_seteuid(0)||krb5_seteuid(target_uid)) {
com_err("ksu", errno, "while switching to target uid");
return FALSE;
}
fprintf(stderr,"WARNING: Your password may be exposed if you enter it here and are logged \n");
fprintf(stderr," in remotely using an unsecure (non-encrypted) channel. \n");
if (krb5_get_tkt_via_passwd (context, &cc, client, tgtq.server,
options, & zero_password) == FALSE){
krb5_seteuid(0);
return FALSE;
}
*path_passwd = 1;
if (krb5_seteuid(0)) {
com_err("ksu", errno, "while reclaiming root uid");
return FALSE;
}
#else
plain_dump_principal (context, client);
fprintf(stderr,"does not have any appropriate tickets in the cache.\n");
return FALSE;
#endif
}
if ((retval= krb5_copy_principal(context, client, &in_creds.client))){
com_err(prog_name, retval,"while copying client principal");
return (FALSE) ;
}
if ((retval= krb5_copy_principal(context, server, &in_creds.server))){
com_err(prog_name, retval,"while copying client principal");
return (FALSE) ;
}
if ((retval = krb5_get_cred_from_kdc(context, cc, &in_creds,
&out_creds, &tgts))){
com_err(prog_name, retval, "while geting credentials from kdc");
return (FALSE);
}
if (auth_debug){
fprintf(stderr,"krb5_auth_check: got ticket for end server \n");
dump_principal(context, "out_creds->server", out_creds->server );
}
if (tgts){
register int i =0;
if (auth_debug){
fprintf(stderr, "krb5_auth_check: went via multiple realms");
}
while (tgts[i]){
if ((retval=krb5_cc_store_cred(context,cc,tgts[i]))) {
com_err(prog_name, retval,
"while storing credentials from cross-realm walk");
return (FALSE);
}
i++;
}
krb5_free_tgt_creds(context, tgts);
}
retval = krb5_verify_tkt_def(context, client, server,
&out_creds->keyblock, &out_creds->ticket,
&target_tkt);
if (retval) {
com_err(prog_name, retval, "while verifying ticket for server");
return (FALSE);
}
if ((retval = krb5_cc_store_cred(context, cc, out_creds))){
com_err(prog_name, retval,
"While storing credentials");
return (FALSE);
}
return (TRUE);
}
krb5_boolean krb5_fast_auth(context, client, server, target_user, cc)
krb5_context context;
krb5_principal client;
krb5_principal server;
char *target_user;
krb5_ccache cc;
{
krb5_creds tgt, tgtq;
krb5_ticket * target_tkt;
krb5_error_code retval;
memset((char *) &tgtq, 0, sizeof(tgtq));
memset((char *) &tgt, 0, sizeof(tgt));
if ((retval= krb5_copy_principal(context, client, &tgtq.client))){
com_err(prog_name, retval,"while copying client principal");
return (FALSE) ;
}
if ((retval= krb5_copy_principal(context, server, &tgtq.server))){
com_err(prog_name, retval,"while copying client principal");
return (FALSE) ;
}
if ((retval = krb5_cc_retrieve_cred(context, cc,
KRB5_TC_MATCH_SRV_NAMEONLY | KRB5_TC_SUPPORTED_KTYPES,
&tgtq, &tgt))){
if (auth_debug)
com_err(prog_name, retval,"While Retrieving credentials");
return (FALSE) ;
}
if ((retval = krb5_verify_tkt_def(context, client, server, &tgt.keyblock,
&tgt.ticket, &target_tkt))){
com_err(prog_name, retval, "while verifing ticket for server");
return (FALSE);
}
return TRUE;
}
static krb5_error_code
krb5_verify_tkt_def(context, client, server, cred_ses_key,
scr_ticket, clear_ticket)
krb5_context context;
krb5_principal client;
krb5_principal server;
krb5_keyblock *cred_ses_key;
krb5_data *scr_ticket;
krb5_ticket **clear_ticket;
{
krb5_keytab keytabid;
krb5_enctype enctype;
krb5_keytab_entry ktentry;
krb5_keyblock *tkt_key = NULL;
krb5_ticket * tkt = NULL;
krb5_error_code retval =0;
krb5_keyblock * tkt_ses_key;
if ((retval = decode_krb5_ticket(scr_ticket, &tkt))){
return retval;
}
if (auth_debug){
fprintf(stderr,"krb5_verify_tkt_def: verifying target server\n");
dump_principal(context, "server", server);
dump_principal(context, "tkt->server", tkt->server);
}
if (server && !krb5_principal_compare(context, server, tkt->server)){
return KRB5KRB_AP_WRONG_PRINC;
}
if ((retval = krb5_kt_default(context, &keytabid))){
krb5_free_ticket(context, tkt);
return retval;
}
enctype = tkt->enc_part.enctype;
if ((retval = krb5_kt_get_entry(context, keytabid, server,
tkt->enc_part.kvno, enctype, &ktentry))){
krb5_free_ticket(context, tkt);
return retval;
}
krb5_kt_close(context, keytabid);
if ((retval = krb5_copy_keyblock(context, &ktentry.key, &tkt_key))){
krb5_free_ticket(context, tkt);
krb5_kt_free_entry(context, &ktentry);
return retval;
}
if ((retval = krb5_decrypt_tkt_part(context, tkt_key, tkt))) {
krb5_free_ticket(context, tkt);
krb5_kt_free_entry(context, &ktentry);
krb5_free_keyblock(context, tkt_key);
return(retval);
}
retval = krb5_check_exp(context, tkt->enc_part2->times);
if (retval) {
if (auth_debug && (retval == KRB5KRB_AP_ERR_TKT_EXPIRED)) {
fprintf(stderr,
"krb5_verify_tkt_def: ticket has expired");
}
krb5_free_ticket(context, tkt);
krb5_kt_free_entry(context, &ktentry);
krb5_free_keyblock(context, tkt_key);
return KRB5KRB_AP_ERR_TKT_EXPIRED;
}
if (!krb5_principal_compare(context, client, tkt->enc_part2->client)) {
krb5_free_ticket(context, tkt);
krb5_kt_free_entry(context, &ktentry);
krb5_free_keyblock(context, tkt_key);
return KRB5KRB_AP_ERR_BADMATCH;
}
if (auth_debug){
fprintf(stderr,
"krb5_verify_tkt_def: verified client's identity\n");
dump_principal(context, "client", client);
dump_principal(context, "tkt->enc_part2->client",tkt->enc_part2->client);
}
tkt_ses_key = tkt->enc_part2->session;
if (cred_ses_key->enctype != tkt_ses_key->enctype ||
cred_ses_key->length != tkt_ses_key->length ||
memcmp((char *)cred_ses_key->contents,
(char *)tkt_ses_key->contents, cred_ses_key->length)) {
krb5_free_ticket(context, tkt);
krb5_kt_free_entry(context, &ktentry);
krb5_free_keyblock(context, tkt_key);
return KRB5KRB_AP_ERR_BAD_INTEGRITY;
}
if (auth_debug){
fprintf(stderr,
"krb5_verify_tkt_def: session keys match \n");
}
*clear_ticket = tkt;
krb5_kt_free_entry(context, &ktentry);
krb5_free_keyblock(context, tkt_key);
return 0;
}
krb5_boolean krb5_get_tkt_via_passwd (context, ccache, client, server,
options, zero_password)
krb5_context context;
krb5_ccache *ccache;
krb5_principal client;
krb5_principal server;
opt_info *options;
krb5_boolean *zero_password;
{
krb5_error_code code;
krb5_creds my_creds;
krb5_timestamp now;
unsigned int pwsize;
char password[255], *client_name, prompt[255];
*zero_password = FALSE;
if ((code = krb5_unparse_name(context, client, &client_name))) {
com_err (prog_name, code, "when unparsing name");
return (FALSE);
}
memset((char *)&my_creds, 0, sizeof(my_creds));
if ((code = krb5_copy_principal(context, client, &my_creds.client))){
com_err (prog_name, code, "while copying principal");
return (FALSE);
}
if ((code = krb5_copy_principal(context, server, &my_creds.server))){
com_err (prog_name, code, "while copying principal");
return (FALSE);
}
if ((code = krb5_timeofday(context, &now))) {
com_err(prog_name, code, "while getting time of day");
return (FALSE);
}
my_creds.times.starttime = 0;
my_creds.times.endtime = now + options->lifetime;
if (options->opt & KDC_OPT_RENEWABLE) {
my_creds.times.renew_till = now + options->rlife;
} else
my_creds.times.renew_till = 0;
if (strlen (client_name) + 80 > sizeof (prompt)) {
fprintf (stderr,
"principal name %s too long for internal buffer space\n",
client_name);
return FALSE;
}
(void) sprintf(prompt,"Kerberos password for %s: ", client_name);
pwsize = sizeof(password);
code = krb5_read_password(context, prompt, 0, password, &pwsize);
if (code ) {
com_err(prog_name, code, "while reading password for '%s'\n",
client_name);
memset(password, 0, sizeof(password));
return (FALSE);
}
if ( pwsize == 0) {
fprintf(stderr, "No password given\n");
*zero_password = TRUE;
memset(password, 0, sizeof(password));
return (FALSE);
}
code = krb5_get_in_tkt_with_password(context, options->opt,
0, NULL, preauth_ptr,
password, *ccache, &my_creds, 0);
memset(password, 0, sizeof(password));
if (code) {
if (code == KRB5KRB_AP_ERR_BAD_INTEGRITY)
fprintf (stderr, "%s: Password incorrect\n", prog_name);
else
com_err (prog_name, code, "while getting initial credentials");
return (FALSE);
}
return (TRUE);
}
void dump_principal (context, str, p)
krb5_context context;
char *str;
krb5_principal p;
{
char * stname;
krb5_error_code retval;
if ((retval = krb5_unparse_name(context, p, &stname))) {
fprintf(stderr, " %s while unparsing name\n", error_message(retval));
}
fprintf(stderr, " %s: %s\n", str, stname);
}
void plain_dump_principal (context, p)
krb5_context context;
krb5_principal p;
{
char * stname;
krb5_error_code retval;
if ((retval = krb5_unparse_name(context, p, &stname)))
fprintf(stderr, " %s while unparsing name\n", error_message(retval));
fprintf(stderr, "%s ", stname);
}
krb5_error_code get_best_principal(context, plist, client)
krb5_context context;
char **plist;
krb5_principal *client;
{
krb5_error_code retval =0;
krb5_principal temp_client, best_client = NULL;
int i = 0, nelem;
if (! plist ) return 0;
nelem = krb5_princ_size(context, *client);
while(plist[i]){
if ((retval = krb5_parse_name(context, plist[i], &temp_client))){
return retval;
}
if (data_eq(*krb5_princ_realm(context, *client),
*krb5_princ_realm(context, temp_client))) {
if (nelem &&
krb5_princ_size(context, *client) > 0 &&
krb5_princ_size(context, temp_client) > 0) {
krb5_data *p1 =
krb5_princ_component(context, *client, 0);
krb5_data *p2 =
krb5_princ_component(context, temp_client, 0);
if (data_eq(*p1, *p2)) {
if (auth_debug){
fprintf(stderr,
"get_best_principal: compare with %s\n",
plist[i]);
}
if(best_client){
if(krb5_princ_size(context, best_client) >
krb5_princ_size(context, temp_client)){
best_client = temp_client;
}
}else{
best_client = temp_client;
}
}
}
}
i++;
}
if (best_client) *client = best_client;
return 0;
}