#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <locale.h>
#include <pwd.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include "k5dce.h"
#include <dce/sec_login.h>
#include <dce/dce_error.h>
#include <dce/passwd.h>
#if defined(DEBUG)
#define DEEDEBUG(A) fprintf(stderr,A); fflush(stderr)
#define DEEDEBUG2(A,B) fprintf(stderr,A,B); fflush(stderr)
#else
#define DEEDEBUG(A)
#define DEEDEBUG2(A,B)
#endif
#ifdef __hpux
#define seteuid(A) setresuid(-1,A,-1)
#endif
int k5dcecreate (uid_t, char *, char*, krb5_creds **);
int k5dcecon (uid_t, char *, char *);
int k5dcegettgt (krb5_ccache *, char *, char *, krb5_creds **);
int k5dcematch (uid_t, char *, char *, off_t *, krb5_creds **);
int k5dcesession (uid_t, char *, krb5_creds **, int *,krb5_flags);
char *progname = "k5dcecon";
static time_t now;
#ifdef notdef
#ifdef _AIX
void com_err(char *p1, int code, ...)
{
int lst;
dce_error_string_t err_string;
dce_error_inq_text(code, err_string, &lst);
fprintf(stderr,"Error %d in %s: %s\n", code, p1, err_string );
}
void krb5_init_ets()
{
}
#endif
#endif
int k5dcesession(luid, pname, tgt, ppag, tflags)
uid_t luid;
char *pname;
krb5_creds **tgt;
int *ppag;
krb5_flags tflags;
{
DIR *dirp;
struct dirent *direntp;
off_t size;
krb5_timestamp endtime;
int better = 0;
krb5_creds *xtgt;
char prev_name[17] = "";
krb5_timestamp prev_endtime;
off_t prev_size;
u_long prev_pag = 0;
char ccname[64] = "FILE:/opt/dcelocal/var/security/creds/";
error_status_t st;
sec_login_handle_t lcontext = 0;
dce_error_string_t err_string;
int lst;
DEEDEBUG2("k5dcesession looking for flags %8.8x\n",tflags);
dirp = opendir("/opt/dcelocal/var/security/creds/");
if (dirp == NULL) {
return 1;
}
while ( (direntp = readdir( dirp )) != NULL ) {
if (!strncmp(direntp->d_name,"dcecred_",8)
&& (strlen(direntp->d_name) == 16)) {
strcpy(ccname+38,direntp->d_name);
if (!k5dcematch(luid, pname, ccname, &size, &xtgt)) {
DEEDEBUG2("Cache:%s",direntp->d_name);
DEEDEBUG2(" size:%d",size);
DEEDEBUG2(" flags:%8.8x",xtgt->ticket_flags);
DEEDEBUG2(" %s",ctime((time_t *)&xtgt->times.endtime));
if ((xtgt->ticket_flags & tflags) == tflags ) {
if (prev_name[0]) {
if (xtgt->times.endtime > prev_endtime) {
better = 1;
} else if ((xtgt->times.endtime = prev_endtime)
&& (size > prev_size)){
better = 1;
}
} else {
if (xtgt->times.endtime >= now) {
better = 1;
}
}
if (better) {
strcpy(prev_name, direntp->d_name);
prev_endtime = xtgt->times.endtime;
prev_size = size;
sscanf(prev_name+8,"%8X",&prev_pag);
*tgt = xtgt;
better = 0;
}
}
}
}
}
(void)closedir( dirp );
if (!prev_name[0])
return 1;
DEEDEBUG2("Best: %s\n",prev_name);
if (ppag)
*ppag = prev_pag;
strcpy(ccname+38,prev_name);
setenv("KRB5CCNAME",ccname,1);
return(0);
}
int k5dcematch(luid, pname, ccname, sizep, tgt)
uid_t luid;
char *pname;
char *ccname;
off_t *sizep;
krb5_creds **tgt;
{
krb5_ccache cache;
struct stat stbuf;
char ccdata[256];
int fd;
int status;
if (!strncmp(ccname,"FILE:",5)) {
strcpy(ccdata,ccname+5);
strcat(ccdata,".data");
if (stat(ccdata, &stbuf))
return(1);
if (stbuf.st_uid != luid)
return(1);
if ((fd = open(ccdata,O_RDONLY)) == -1)
return(1);
if ((read(fd,&status,4)) != 4) {
close(fd);
return(1);
}
if (status != 5)
return(1);
if (stat(ccname+5, &stbuf))
return(1);
if (stbuf.st_uid != luid)
return(1);
*sizep = stbuf.st_size;
}
return(k5dcegettgt(&cache, ccname, pname, tgt));
}
int k5dcegettgt(pcache, ccname, pname, tgt)
krb5_ccache *pcache;
char *ccname;
char *pname;
krb5_creds **tgt;
{
krb5_ccache cache;
krb5_cc_cursor cur;
krb5_creds creds;
int code;
int found = 1;
krb5_principal princ;
char *kusername;
krb5_flags flags;
char *sname, *realm, *tgtname = NULL;
setenv("KRB5CCNAME",ccname,1);
cache = NULL;
*tgt = NULL;
if (code = krb5_cc_default(pcache)) {
com_err(progname, code, "while getting ccache");
goto return2;
}
DEEDEBUG("Got cache\n");
flags = 0;
if (code = krb5_cc_set_flags(*pcache, flags)) {
com_err(progname, code,"While setting flags");
goto return2;
}
DEEDEBUG("Set flags\n");
if (code = krb5_cc_get_principal(*pcache, &princ)) {
com_err(progname, code, "While getting princ");
goto return1;
}
DEEDEBUG("Got principal\n");
if (code = krb5_unparse_name(princ, &kusername)) {
com_err(progname, code, "While unparsing principal");
goto return1;
}
DEEDEBUG2("Unparsed to \"%s\"\n", kusername);
DEEDEBUG2("pname is \"%s\"\n", pname);
if (strcmp(kusername, pname)) {
DEEDEBUG("Principals not equal\n");
goto return1;
}
DEEDEBUG("Principals equal\n");
realm = strchr(pname,'@');
realm++;
if ((tgtname = malloc(9 + 2 * strlen(realm))) == 0) {
fprintf(stderr,"Malloc failed for tgtname\n");
goto return1;
}
strcpy(tgtname,"krbtgt/");
strcat(tgtname,realm);
strcat(tgtname,"@");
strcat(tgtname,realm);
DEEDEBUG2("Getting tgt %s\n", tgtname);
if (code = krb5_cc_start_seq_get(*pcache, &cur)) {
com_err(progname, code, "while starting to retrieve tickets");
goto return1;
}
while (!(code = krb5_cc_next_cred(*pcache, &cur, &creds))) {
krb5_creds *cred = &creds;
if (code = krb5_unparse_name(cred->server, &sname)) {
com_err(progname, code, "while unparsing server name");
continue;
}
if (strncmp(sname, tgtname, strlen(tgtname)) == 0) {
DEEDEBUG("FOUND\n");
if (code = krb5_copy_creds(&creds, tgt)) {
com_err(progname, code, "while copying TGT");
goto return1;
}
found = 0;
break;
}
}
if (code = krb5_cc_end_seq_get(*pcache, &cur)) {
com_err(progname, code, "while finishing retrieval");
goto return2;
}
return1:
flags = KRB5_TC_OPENCLOSE;
krb5_cc_set_flags(*pcache, flags);
return2:
if (tgtname)
free(tgtname);
return(found);
}
int k5dcecon(luid, luser, pname)
uid_t luid;
char *luser;
char *pname;
{
krb5_creds *ftgt = NULL;
krb5_creds *tgt = NULL;
unsigned32 dfspag;
boolean32 reset_passwd = 0;
int lst;
dce_error_string_t err_string;
char *shell_prog;
krb5_ccache fcache;
char *ccname;
char *kusername;
char *urealm;
char *cp;
int pag;
int code;
krb5_timestamp endtime;
if ((ccname = getenv("KRB5CCNAME")) == NULL) {
DEEDEBUG("No KRB5CCNAME\n");
return(1);
}
if (k5dcegettgt(&fcache, ccname, pname, &ftgt)) {
fprintf(stderr, "%s: Did not find TGT\n", progname);
return(1);
}
DEEDEBUG2("flags=%x\n",ftgt->ticket_flags);
if (!(ftgt->ticket_flags & TKT_FLG_FORWARDABLE)){
fprintf(stderr,"Ticket not forwardable\n");
return(0);
}
setenv("KRB5CCNAME","",1);
#define TKT_ACCEPTABLE (TKT_FLG_FORWARDABLE | TKT_FLG_PROXIABLE \
| TKT_FLG_MAY_POSTDATE | TKT_FLG_RENEWABLE | TKT_FLG_HW_AUTH \
| TKT_FLG_PRE_AUTH)
if (!k5dcesession(luid, pname, &tgt, &pag,
(ftgt->ticket_flags & TKT_ACCEPTABLE))) {
if (ftgt->times.endtime > tgt->times.endtime) {
DEEDEBUG("Updating existing cache\n");
return(k5dceupdate(&ftgt, pag));
} else {
DEEDEBUG("Using existing cache\n");
return(0);
}
}
if ((code = k5dcecreate(luid, luser, pname, &ftgt))) {
return (code);
}
DEEDEBUG("Destroying the old cache\n");
if ((code = krb5_cc_destroy(fcache))) {
com_err(progname, code, "while destroying Kerberos5 ccache");
}
return (0);
}
int k5dceupdate(krbtgt, pag)
krb5_creds **krbtgt;
int pag;
{
krb5_ccache ccache;
int code;
if (code = krb5_cc_default(&ccache)) {
com_err(progname, code, "while opening cache for update");
return(2);
}
if (code = ccache->ops->init(ccache,(*krbtgt)->client)) {
com_err(progname, code, "while reinitilizing cache");
return(3);
}
if (code = ccache->ops->store(ccache, *krbtgt)) {
com_err(progname, code, "while updating cache");
return(2);
}
sec_login_pag_new_tgt(pag, (*krbtgt)->times.endtime);
return(0);
}
int k5dcecreate(luid, luser, pname, krbtgt)
uid_t luid;
char *luser;
char *pname;
krb5_creds **krbtgt;
{
char *cp;
char *urealm;
char *username;
char *defrealm;
uid_t uid;
error_status_t st;
sec_login_handle_t lcontext = 0;
sec_login_auth_src_t auth_src = 0;
boolean32 reset_passwd = 0;
int lst;
dce_error_string_t err_string;
setenv("KRB5CCNAME","",1);
uid = getuid();
DEEDEBUG2("uid=%d\n",uid);
if (uid == 0) {
if (seteuid(luid) < 0)
goto abort;
}
cp = strchr(pname,'@');
*cp = '\0';
urealm = ++cp;
DEEDEBUG2("basename=%s\n",cp);
DEEDEBUG2("realm=%s\n",urealm);
if ((username = malloc(7+strlen(pname)+strlen(urealm))) == 0) {
fprintf(stderr,"Malloc failed for username\n");
goto abort;
}
if (krb5_get_default_realm(&defrealm)) {
DEEDEBUG("krb5_get_default_realm failed\n");
goto abort;
}
if (!strcmp(urealm,defrealm)) {
strcpy(username,pname);
} else {
strcpy(username,"/.../");
strcat(username,urealm);
strcat(username,"/");
strcat(username,pname);
}
if (sec_login_setup_identity((unsigned_char_p_t)username,
(sec_login_external_tgt|sec_login_proxy_cred),
&lcontext, &st)) {
DEEDEBUG("Adding our new TGT\n");
sec_login_krb5_add_cred(lcontext, *krbtgt, &st);
if (st) {
dce_error_inq_text(st, err_string, &lst);
fprintf(stderr,
"Error while adding credentials for %s because %s\n",
username, err_string);
goto abort;
}
DEEDEBUG("validating and certifying\n");
if (sec_login_validate_identity(lcontext, 0, &reset_passwd,
&auth_src, &st)) {
DEEDEBUG2("validate_identity st=%d\n",st);
if (st) {
dce_error_inq_text(st, err_string, &lst);
fprintf(stderr, "Validation error for %s because %s\n",
username, err_string);
goto abort;
}
if (!sec_login_certify_identity(lcontext,&st)) {
dce_error_inq_text(st, err_string, &lst);
fprintf(stderr,
"Credentials not certified because %s\n",err_string);
}
if (reset_passwd) {
fprintf(stderr,
"Password must be changed for %s\n", username);
}
if (auth_src == sec_login_auth_src_local) {
fprintf(stderr,
"Credentials obtained from local registry for %s\n",
username);
}
if (auth_src == sec_login_auth_src_overridden) {
fprintf(stderr, "Validated %s from local override entry, no network credentials obtained\n", username);
goto abort;
}
DEEDEBUG("Ceating new cred files.\n");
sec_login_set_context(lcontext, &st);
if (st) {
dce_error_inq_text(st, err_string, &lst);
fprintf(stderr,
"Unable to set context for %s because %s\n",
username, err_string);
goto abort;
}
#if 0
sec_login_release_context(&lcontext, &st);
if (st) {
dce_error_inq_text(st, err_string, &lst);
fprintf(stderr,
"Unable to release context for %s because %s\n",
username, err_string);
goto abort;
}
#endif
}
else {
DEEDEBUG2("validate failed %d\n",st);
dce_error_inq_text(st, err_string, &lst);
fprintf(stderr,
"Unable to validate %s because %s\n", username,
err_string);
goto abort;
}
}
else {
dce_error_inq_text(st, err_string, &lst);
fprintf(stderr,
"Unable to setup login entry for %s because %s\n",
username, err_string);
goto abort;
}
done:
DEEDEBUG2("sec_login_inq_pag %8.8x\n",
sec_login_inq_pag(lcontext, &st));
if (uid == 0) {
seteuid(0);
}
DEEDEBUG("completed\n");
return(0);
abort:
if (uid == 0) {
seteuid(0);
}
DEEDEBUG("Aborting\n");
return(2);
}
main(argc, argv)
int argc;
char *argv[];
{
int status;
extern int optind;
extern char *optarg;
int rv;
char *lusername = NULL;
char *pname = NULL;
int fflag = 0;
struct passwd *pw;
uid_t luid;
uid_t myuid;
char *ccname;
krb5_creds *tgt = NULL;
#ifdef DEBUG
close(2);
open("/tmp/k5dce.debug",O_WRONLY|O_CREAT|O_APPEND, 0600);
#endif
if (myuid = getuid()) {
DEEDEBUG2("UID = %d\n",myuid);
exit(33);
}
while ((rv = getopt(argc,argv,"l:p:fs")) != -1) {
DEEDEBUG2("Arg = %c\n", rv);
switch(rv) {
case 'l':
lusername = optarg;
DEEDEBUG2("Optarg = %s\n", optarg);
break;
case 'p':
pname = optarg;
DEEDEBUG2("Optarg = %s\n", optarg);
break;
case 'f':
fflag++;
break;
case 's':
break;
}
}
setlocale(LC_ALL, "");
krb5_init_ets();
time(&now);
if (!lusername) {
lusername = getenv("USER");
if (!lusername) {
fprintf(stderr, "USER not in environment\n");
return(3);
}
}
if ((pw = getpwnam(lusername)) == NULL) {
fprintf(stderr, "Who are you?\n");
return(44);
}
luid = pw->pw_uid;
if (fflag) {
status = k5dcecon(luid, lusername, pname);
} else {
status = k5dcesession(luid, pname, &tgt, NULL, 0);
}
if (!status) {
printf("%s",getenv("KRB5CCNAME"));
DEEDEBUG2("KRB5CCNAME=%s\n",getenv("KRB5CCNAME"));
}
DEEDEBUG2("Returning status %d\n",status);
return (status);
}