/*
* RatPwCache.c --
*
* This file contains password caching routines
*
* TkRat software and its included text is Copyright 1996-2002 by
* Martin Forssén
*
* The full text of the legal notice is contained in the file called
* COPYRIGHT, included with this distribution.
*/
#include <sys/stat.h>
#include <unistd.h>
#include "rat.h"
#include "env_unix.h"
/*
* For the memory cache
*/
typedef struct CachedPasswd {
int onDisk;
char *spec;
char *passwd;
struct CachedPasswd *next;
Tcl_TimerToken token;
} CachedPasswd;
static CachedPasswd *cache = NULL;
static int initialized = 0;
static char *filename = NULL;
/*
* Local functions
*/
static char *Canonify(const char *spec);
static void ReadDisk(Tcl_Interp *interp);
static void WriteDisk(Tcl_Interp *interp);
static void TouchEntry(Tcl_Interp *interp, CachedPasswd *cp);
static Tcl_TimerProc ErasePasswd;
/*
*----------------------------------------------------------------------
*
* Canonify --
*
* Convert foler specification to canonical form
*
* Results:
* A pointer to a static buffer containing the canonic form.
* This buffer will be rewritten by the next call.
*
* Side effects:
* None.
*
*
*----------------------------------------------------------------------
*/
static char*
Canonify(const char *spec)
{
static char *cSpec = NULL;
static int size = 0;
char *c;
if (strlen(spec)+1 > size) {
size = strlen(spec)+64;
cSpec = (char*)realloc(cSpec, size);
}
strlcpy(cSpec, spec, size);
if (NULL != (c = strstr(cSpec, "/debug"))) {
memmove(c, c+6, strlen(c+6)+1);
}
if (NULL != (c = strchr(cSpec, '}'))) {
c[1] = '\0';
}
return cSpec;
}
/*
*----------------------------------------------------------------------
*
* ReadDisk --
*
* Read cached passwords from disk
*
* Results:
* None.
*
* Side effects:
* Updates the local cached list
*
*
*----------------------------------------------------------------------
*/
void
ReadDisk(Tcl_Interp *interp)
{
CONST84 char **argv, *spec = NULL, *passwd = NULL;
const char *name;
CachedPasswd *cp;
char buf[1024];
int argc;
FILE *fp;
if (NULL == (name = RatGetPathOption(interp, "pwcache_file"))) {
return;
}
filename = cpystr(name);
initialized = 1;
if (NULL == (fp = fopen(filename, "r"))) {
return;
}
while (fgets(buf, sizeof(buf), fp), !feof(fp)) {
if (TCL_OK != Tcl_SplitList(interp, buf, &argc, &argv)
|| (argc != 2 && argc != 5)) {
continue;
}
if (2 == argc) {
/* {spec passwd} */
spec = argv[0];
passwd = argv[1];
} else if (5 == argc) {
/* {host port user service passwd} */
snprintf(buf, sizeof(buf), "{%s:%s/user=%s%s}",
argv[0], argv[1], argv[2],
(strcmp("imap", argv[3]) ? "/pop3" : ""));
spec = buf;
passwd = argv[4];
}
cp = (CachedPasswd*)ckalloc(sizeof(CachedPasswd)
+strlen(spec)+1+strlen(passwd)+1);
cp->onDisk = 1;
cp->spec = (char*)cp + sizeof(CachedPasswd);
strcpy(cp->spec, spec);
cp->passwd = cp->spec + strlen(cp->spec)+1;
strcpy(cp->passwd, passwd);
cp->next = cache;
cache = cp;
ckfree(argv);
}
fclose(fp);
return;
}
/*
*----------------------------------------------------------------------
*
* WriteDisk --
*
* Write the cache to disk. Only those entries marked to be stored on
* disk are actually output.
*
* Results:
* None.
*
* Side effects:
* Rewrites the disk cache.
*
*
*----------------------------------------------------------------------
*/
void
WriteDisk(Tcl_Interp *interp)
{
CachedPasswd *cp;
char c;
FILE *fp;
struct stat sbuf;
int i, fd;
Tcl_DString ds;
if (-1 < (fd = open(filename, O_WRONLY))) {
fstat(fd, &sbuf);
c = 0;
for (i=0; i<sbuf.st_size; i++) {
write(fd, &c, 1);
}
close(fd);
unlink(filename);
}
if (NULL == (fp = fopen(filename, "w"))) {
return;
}
fchmod(fileno(fp), 0600);
Tcl_DStringInit(&ds);
for (cp = cache; cp; cp = cp->next) {
if (cp->onDisk) {
Tcl_DStringAppendElement(&ds, cp->spec);
Tcl_DStringAppendElement(&ds, cp->passwd);
fprintf(fp, "%s\n", Tcl_DStringValue(&ds));
Tcl_DStringSetLength(&ds, 0);
}
}
fclose(fp);
Tcl_DStringFree(&ds);
return;
}
/*
*----------------------------------------------------------------------
*
* TouchEntry --
*
* Touch an entry in the cache. This only affects entries that are
* going to timeout.
*
* Results:
* None.
*
* Side effects:
* May rewrite the cached password file
*
*
*----------------------------------------------------------------------
*/
void
TouchEntry(Tcl_Interp *interp, CachedPasswd *cp)
{
int timeout;
Tcl_Obj *oPtr;
if (cp->onDisk) {
return;
}
Tcl_DeleteTimerHandler(cp->token);
oPtr = Tcl_GetVar2Ex(interp, "option", "cache_passwd_timeout",
TCL_GLOBAL_ONLY);
Tcl_GetIntFromObj(interp, oPtr, &timeout);
if (timeout) {
cp->token =
Tcl_CreateTimerHandler(timeout*1000, ErasePasswd, (ClientData)cp);
}
}
/*
*----------------------------------------------------------------------
*
* ErasePasswd --
*
* Earase a password from the cache
*
* Results:
* None.
*
* Side effects:
* Modifies the in-memory cache
*
*
*----------------------------------------------------------------------
*/
static void
ErasePasswd(ClientData clientData)
{
CachedPasswd **tcp, *cp = (CachedPasswd*)clientData;
Tcl_DeleteTimerHandler(cp->token);
memset(cp->passwd, 0, strlen(cp->passwd));
for (tcp = &cache; *tcp != cp; tcp = &(*tcp)->next);
*tcp = cp->next;
ckfree(cp);
}
/*
*----------------------------------------------------------------------
*
* RatGetCachedPassword --
*
* get a cached password
*
* Results:
* Returns a pointer to a static area containing the password,
* or NULL if no suitable cached password was found.
*
* Side effects:
* may read the cached password file
*
*
*----------------------------------------------------------------------
*/
char*
RatGetCachedPassword(Tcl_Interp *interp, const char *spec)
{
CachedPasswd *cp;
char *cSpec = Canonify(spec);
if (0 == initialized) {
ReadDisk(interp);
}
for (cp = cache; cp; cp = cp->next) {
if (!strcmp(cp->spec, cSpec)) {
TouchEntry(interp, cp);
return cp->passwd;
}
}
return NULL;
}
/*
*----------------------------------------------------------------------
*
* RatCachePassword --
*
* Cache a password
*
* Results:
* None.
*
* Side effects:
* May rewrite the cached password file
*
*
*----------------------------------------------------------------------
*/
void
RatCachePassword(Tcl_Interp *interp, const char *spec, const char *passwd,
int store)
{
CachedPasswd *cp;
char *cSpec = Canonify(spec);
if (0 == initialized) {
ReadDisk(interp);
}
cp = (CachedPasswd*)ckalloc(sizeof(CachedPasswd) +
strlen(cSpec) + 1 + strlen(passwd) + 1);
cp->onDisk = store;
cp->spec = (char*)cp + sizeof(CachedPasswd);
strcpy(cp->spec, cSpec);
cp->passwd = cp->spec+strlen(cSpec)+1;
strcpy(cp->passwd, passwd);
cp->next = cache;
cp->token = NULL;
cache = cp;
if (store) {
WriteDisk(interp);
} else {
TouchEntry(interp, cp);
}
return;
}
/*
*----------------------------------------------------------------------
*
* RatPasswdCachePurge --
*
* Purge the password cache. If disk_also is true the both the disk and
* memory caches are purged. If disk_also is false, the only the
* memory cache is purged.
*
* Results:
* None.
*
* Side effects:
* May rewrite the cached password file
*
*
*----------------------------------------------------------------------
*/
void
RatPasswdCachePurge(Tcl_Interp *interp, int disk_also)
{
CachedPasswd *cp, *cpn;
if (0 == initialized) {
ReadDisk(interp);
}
for (cp = cache; cp; cp = cpn) {
cpn = cp->next;
memset(cp->passwd, 0, strlen(cp->passwd));
Tcl_DeleteTimerHandler(cp->token);
ckfree(cp);
}
cache = NULL;
if (disk_also) {
WriteDisk(interp);
}
return;
}
int
RatPasswdCachePurgeCmd(ClientData clientData, Tcl_Interp *interp, int objc,
Tcl_Obj *CONST objv[])
{
RatPasswdCachePurge(interp, 1);
return TCL_OK;
}
syntax highlighted by Code2HTML, v. 0.9.1