#include "krb.h"
#include "k5-int.h"
#include "krb4int.h"
#include <stdio.h>
#include <string.h>
#include <errno.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <sys/stat.h>
#include <fcntl.h>
#ifdef TKT_SHMEM
#include <sys/param.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#endif
#define TOO_BIG -1
#define TF_LCK_RETRY ((unsigned)2)
extern int krb_debug;
void tf_close();
#ifdef TKT_SHMEM
char *krb_shm_addr;
static char *tmp_shm_addr;
static const char krb_dummy_skey[8];
char *shmat();
#endif
#ifdef NEED_UTIMES
#include <sys/time.h>
#ifdef __SCO__
#include <utime.h>
#endif
#if defined(__svr4__) || defined(__SVR4)
#include <utime.h>
#endif
int utimes(path, times)
char* path;
struct timeval times[2];
{
struct utimbuf tv;
tv.actime = times[0].tv_sec;
tv.modtime = times[1].tv_sec;
return utime(path,&tv);
}
#endif
#ifdef HAVE_SETEUID
#define do_seteuid(e) seteuid((e))
#else
#ifdef HAVE_SETRESUID
#define do_seteuid(e) setresuid(-1, (e), -1)
#else
#ifdef HAVE_SETREUID
#define do_seteuid(e) setreuid(geteuid(), (e))
#else
#define do_seteuid(e) (errno = EPERM, -1)
#endif
#endif
#endif
static int fd = -1;
static int curpos;
static int lastpos;
static char tfbfr[BUFSIZ];
static int tf_gets (char *, int), tf_read (char *, int);
int KRB5_CALLCONV tf_init(tf_name, rw)
const char *tf_name;
int rw;
{
int wflag;
uid_t me, metoo;
struct stat stat_buf, stat_buffd;
#ifdef TKT_SHMEM
char shmidname[MAXPATHLEN];
FILE *sfp;
int shmid;
#endif
if (!krb5__krb4_context) {
if (krb5_init_context(&krb5__krb4_context))
return TKT_FIL_LCK;
}
me = getuid();
metoo = geteuid();
switch (rw) {
case R_TKT_FIL:
wflag = 0;
break;
case W_TKT_FIL:
wflag = 1;
break;
default:
if (krb_debug) fprintf(stderr, "tf_init: illegal parameter\n");
return TKT_FIL_ACC;
}
if (tf_name == 0)
tf_name = tkt_string();
#ifdef TKT_SHMEM
(void) strncpy(shmidname, tf_name, sizeof(shmidname) - 1);
shmidname[sizeof(shmidname) - 1] = '\0';
(void) strncat(shmidname, ".shm", sizeof(shmidname) - 1 - strlen(shmidname));
#endif
curpos = sizeof(tfbfr);
#ifdef TKT_SHMEM
if (lstat(shmidname, &stat_buf) < 0) {
switch (errno) {
case ENOENT:
return NO_TKT_FIL;
default:
return TKT_FIL_ACC;
}
}
if (stat_buf.st_uid != me || !(stat_buf.st_mode & S_IFREG)
|| stat_buf.st_nlink != 1 || stat_buf.st_mode & 077) {
return TKT_FIL_ACC;
}
if (me != metoo && do_seteuid(me) < 0)
return KFAILURE;
sfp = fopen(shmidname, "r");
if (me != metoo && do_seteuid(metoo) < 0)
return KFAILURE;
if (sfp == 0) {
switch(errno) {
case ENOENT:
return NO_TKT_FIL;
default:
return TKT_FIL_ACC;
}
}
if (fstat(fileno(sfp), &stat_buffd) < 0) {
(void) close(fd);
fd = -1;
switch(errno) {
case ENOENT:
return NO_TKT_FIL;
default:
return TKT_FIL_ACC;
}
}
if ((stat_buf.st_ino != stat_buffd.st_ino) ||
(stat_buf.st_dev != stat_buffd.st_dev)) {
(void) close(fd);
fd = -1;
return TKT_FIL_ACC;
}
if ((stat_buffd.st_uid != me && me != 0) ||
((stat_buffd.st_mode & S_IFMT) != S_IFREG)) {
(void) close(fd);
fd = -1;
return TKT_FIL_ACC;
}
shmid = -1;
{
char buf[BUFSIZ];
int val;
setbuf(sfp, buf);
if ((val = fscanf(sfp,"%d",&shmid)) != 1) {
(void) fclose(sfp);
return TKT_FIL_ACC;
}
if (shmid < 0) {
(void) fclose(sfp);
return TKT_FIL_ACC;
}
(void) fclose(sfp);
}
if (!krb_shm_addr) {
if ((krb_shm_addr = shmat(shmid,0,0)) == -1){
if (krb_debug)
fprintf(stderr,
"cannot attach shared memory for segment %d\n",
shmid);
krb_shm_addr = 0;
return TKT_FIL_ACC;
}
}
tmp_shm_addr = krb_shm_addr;
#endif
if (lstat(tf_name, &stat_buf) < 0) {
switch (errno) {
case ENOENT:
return NO_TKT_FIL;
default:
return TKT_FIL_ACC;
}
}
if (stat_buf.st_uid != me || !(stat_buf.st_mode & S_IFREG)
|| stat_buf.st_nlink != 1 || stat_buf.st_mode & 077) {
return TKT_FIL_ACC;
}
if (wflag) {
if (me != metoo && do_seteuid(me) < 0)
return KFAILURE;
fd = open(tf_name, O_RDWR, 0600);
if (me != metoo && do_seteuid(metoo) < 0)
return KFAILURE;
if (fd < 0) {
switch(errno) {
case ENOENT:
return NO_TKT_FIL;
default:
return TKT_FIL_ACC;
}
}
if (fstat(fd, &stat_buffd) < 0) {
(void) close(fd);
fd = -1;
switch(errno) {
case ENOENT:
return NO_TKT_FIL;
default:
return TKT_FIL_ACC;
}
}
if ((stat_buf.st_ino != stat_buffd.st_ino) ||
(stat_buf.st_dev != stat_buffd.st_dev)) {
(void) close(fd);
fd = -1;
return TKT_FIL_ACC;
}
if ((stat_buffd.st_uid != me && me != 0) ||
((stat_buffd.st_mode & S_IFMT) != S_IFREG)) {
(void) close(fd);
fd = -1;
return TKT_FIL_ACC;
}
if (krb5_lock_file(krb5__krb4_context, fd,
KRB5_LOCKMODE_EXCLUSIVE |
KRB5_LOCKMODE_DONTBLOCK) < 0) {
sleep(TF_LCK_RETRY);
if (krb5_lock_file(krb5__krb4_context, fd,
KRB5_LOCKMODE_EXCLUSIVE |
KRB5_LOCKMODE_DONTBLOCK) < 0) {
(void) close(fd);
fd = -1;
return TKT_FIL_LCK;
}
}
return KSUCCESS;
}
if (me != metoo && do_seteuid(me) < 0)
return KFAILURE;
fd = open(tf_name, O_RDONLY, 0600);
if (me != metoo && do_seteuid(metoo) < 0)
return KFAILURE;
if (fd < 0) {
switch(errno) {
case ENOENT:
return NO_TKT_FIL;
default:
return TKT_FIL_ACC;
}
}
if (fstat(fd, &stat_buffd) < 0) {
(void) close(fd);
fd = -1;
switch(errno) {
case ENOENT:
return NO_TKT_FIL;
default:
return TKT_FIL_ACC;
}
}
if ((stat_buf.st_ino != stat_buffd.st_ino) ||
(stat_buf.st_dev != stat_buffd.st_dev)) {
(void) close(fd);
fd = -1;
return TKT_FIL_ACC;
}
if ((stat_buffd.st_uid != me && me != 0) ||
((stat_buffd.st_mode & S_IFMT) != S_IFREG)) {
(void) close(fd);
fd = -1;
return TKT_FIL_ACC;
}
if (krb5_lock_file(krb5__krb4_context, fd,
KRB5_LOCKMODE_SHARED |
KRB5_LOCKMODE_DONTBLOCK) < 0) {
sleep(TF_LCK_RETRY);
if (krb5_lock_file(krb5__krb4_context, fd,
KRB5_LOCKMODE_SHARED |
KRB5_LOCKMODE_DONTBLOCK) < 0) {
(void) close(fd);
fd = -1;
return TKT_FIL_LCK;
}
}
return KSUCCESS;
}
int KRB5_CALLCONV tf_get_pname(p)
char *p;
{
if (fd < 0) {
if (krb_debug)
fprintf(stderr, "tf_get_pname called before tf_init.\n");
return TKT_FIL_INI;
}
if (tf_gets(p, ANAME_SZ) < 2)
return TKT_FIL_FMT;
return KSUCCESS;
}
int KRB5_CALLCONV tf_get_pinst(inst)
char *inst;
{
if (fd < 0) {
if (krb_debug)
fprintf(stderr, "tf_get_pinst called before tf_init.\n");
return TKT_FIL_INI;
}
if (tf_gets(inst, INST_SZ) < 1)
return TKT_FIL_FMT;
return KSUCCESS;
}
int KRB5_CALLCONV tf_get_cred(c)
CREDENTIALS *c;
{
KTEXT ticket = &c->ticket_st;
int k_errno;
long issue_date;
if (fd < 0) {
if (krb_debug)
fprintf(stderr, "tf_get_cred called before tf_init.\n");
return TKT_FIL_INI;
}
if ((k_errno = tf_gets(c->service, SNAME_SZ)) < 2)
switch (k_errno) {
case TOO_BIG:
case 1:
tf_close();
return TKT_FIL_FMT;
case 0:
return EOF;
}
if ((k_errno = tf_gets(c->instance, INST_SZ)) < 1)
switch (k_errno) {
case TOO_BIG:
return TKT_FIL_FMT;
case 0:
return EOF;
}
if ((k_errno = tf_gets(c->realm, REALM_SZ)) < 2)
switch (k_errno) {
case TOO_BIG:
case 1:
tf_close();
return TKT_FIL_FMT;
case 0:
return EOF;
}
if (
tf_read((char *) (c->session), KEY_SZ) < 1 ||
tf_read((char *) &(c->lifetime), sizeof(c->lifetime)) < 1 ||
tf_read((char *) &(c->kvno), sizeof(c->kvno)) < 1 ||
tf_read((char *) &(ticket->length), sizeof(ticket->length))
< 1 ||
ticket->length > MAX_KTXT_LEN ||
tf_read((char *) (ticket->dat), ticket->length) < 1 ||
tf_read((char *) &(issue_date), sizeof(issue_date)) < 1
) {
tf_close();
return TKT_FIL_FMT;
}
c->issue_date = issue_date;
#ifdef TKT_SHMEM
memcpy(c->session, tmp_shm_addr, KEY_SZ);
tmp_shm_addr += KEY_SZ;
#endif
return KSUCCESS;
}
void KRB5_CALLCONV tf_close()
{
if (!(fd < 0)) {
#ifdef TKT_SHMEM
if (shmdt(krb_shm_addr)) {
if (krb_debug)
fprintf(stderr, "shmdt 0x%x: errno %d",krb_shm_addr, errno);
} else {
krb_shm_addr = 0;
}
#endif
if (!krb5__krb4_context)
krb5_init_context(&krb5__krb4_context);
(void) krb5_lock_file(krb5__krb4_context, fd, KRB5_LOCKMODE_UNLOCK);
(void) close(fd);
fd = -1;
}
memset(tfbfr, 0, sizeof(tfbfr));
}
static int
tf_gets(s, n)
register char *s;
int n;
{
register int count;
if (fd < 0) {
if (krb_debug)
fprintf(stderr, "tf_gets called before tf_init.\n");
return TKT_FIL_INI;
}
for (count = n - 1; count > 0; --count) {
if (curpos >= sizeof(tfbfr)) {
lastpos = read(fd, tfbfr, sizeof(tfbfr));
curpos = 0;
}
if (curpos == lastpos) {
tf_close();
return 0;
}
*s = tfbfr[curpos++];
if (*s++ == '\0')
return (n - count);
}
tf_close();
return TOO_BIG;
}
static int
tf_read(s, n)
register char *s;
register int n;
{
register int count;
for (count = n; count > 0; --count) {
if (curpos >= sizeof(tfbfr)) {
lastpos = read(fd, tfbfr, sizeof(tfbfr));
curpos = 0;
}
if (curpos == lastpos) {
tf_close();
return 0;
}
*s++ = tfbfr[curpos++];
}
return n;
}
int tf_save_cred(service, instance, realm, session, lifetime, kvno,
ticket, issue_date)
char *service;
char *instance;
char *realm;
C_Block session;
int lifetime;
int kvno;
KTEXT ticket;
long issue_date;
{
off_t lseek();
unsigned int count;
#ifdef TKT_SHMEM
int *skey_check;
#endif
if (fd < 0) {
if (krb_debug)
fprintf(stderr, "tf_save_cred called before tf_init.\n");
return TKT_FIL_INI;
}
(void) lseek(fd, (off_t)0, 2);
#ifdef TKT_SHMEM
skey_check = (int *) krb_shm_addr;
while (*skey_check && *(skey_check+1))
skey_check += 2;
tmp_shm_addr = (char *)skey_check;
#endif
count = strlen(service) + 1;
if (write(fd, service, count) != count)
goto bad;
count = strlen(instance) + 1;
if (write(fd, instance, count) != count)
goto bad;
count = strlen(realm) + 1;
if (write(fd, realm, count) != count)
goto bad;
#ifdef TKT_SHMEM
memcpy(tmp_shm_addr, session, 8);
tmp_shm_addr+=8;
if (write(fd,krb_dummy_skey,8) != 8)
goto bad;
#else
if (write(fd, (char *) session, 8) != 8)
goto bad;
#endif
if (write(fd, (char *) &lifetime, sizeof(int)) != sizeof(int))
goto bad;
if (write(fd, (char *) &kvno, sizeof(int)) != sizeof(int))
goto bad;
if (write(fd, (char *) &(ticket->length), sizeof(int)) !=
sizeof(int))
goto bad;
count = ticket->length;
if (write(fd, (char *) (ticket->dat), count) != count)
goto bad;
if (write(fd, (char *) &issue_date, sizeof(long))
!= sizeof(long))
goto bad;
return (KSUCCESS);
bad:
return (KFAILURE);
}