/*
*/
/*
*
* Copyright 1990,1991,1992,1993,1994,2000,2004 Massachusetts Institute of Technology.
* All Rights Reserved.
*
* Original stdio support copyright 1995 by Cygnus Support.
*
* 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.
*
*
* implementation of file-based credentials cache
*/
/*
If OPENCLOSE is defined, each of the functions opens and closes the
file whenever it needs to access it. Otherwise, the file is opened
once in initialize and closed once is close.
This library depends on UNIX-like file descriptors, and UNIX-like
behavior from the functions: open, close, read, write, lseek.
The quasi-BNF grammar for a credentials cache:
file ::=
principal list-of-credentials
credential ::=
client (principal)
server (principal)
keyblock (keyblock)
times (ticket_times)
is_skey (boolean)
ticket_flags (flags)
ticket (data)
second_ticket (data)
principal ::=
number of components (int32)
component 1 (data)
component 2 (data)
...
data ::=
length (int32)
string of length bytes
etc.
*/
/* todo:
Make sure that each time a function returns KRB5_NOMEM, everything
allocated earlier in the function and stack tree is freed.
File locking
fcc_nseq.c and fcc_read don't check return values a lot.
*/
#include "k5-int.h"
#include <syslog.h> /* Solaris Kerberos */
#include <ctype.h>
#include <locale.h>
#include <stdio.h>
#include <errno.h>
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
/* How long to block if flock fails with EAGAIN */
#ifdef HAVE_NETINET_IN_H
#if !defined(_WIN32)
#else
#include "port-sockets.h"
#endif
#else
#endif
static const char * KRB5_CALLCONV krb5_fcc_get_name
krb5_creds *creds);
static krb5_error_code krb5_fcc_read
extern const krb5_cc_ops krb5_cc_file_ops;
krb5_error_code krb5_change_cache (void);
static krb5_error_code krb5_fcc_write
(krb5_context, int);
struct _krb5_fcc_data;
(krb5_context, krb5_ccache, int);
#define KRB5_OK 0
/*
* FCC version 2 contains type information for principals. FCC
* version 1 does not.
*
* FCC version 3 contains keyblock encryption type information, and is
* architecture independent. Previous versions are not.
*
* The code will accept version 1, 2, and 3 ccaches, and depending
* what KRB5_FCC_DEFAULT_FVNO is set to, it will create version 1, 2,
* or 3 FCC caches.
*
* The default credentials cache should be type 3 for now (see
* init_ctx.c).
*/
/* Credential file header tags.
* The header tags are constructed as:
* krb5_ui_2 tag
* krb5_ui_2 len
* krb5_octet data[len]
* This format allows for older versions of the fcc processing code to skip
* past unrecognized tag formats.
*/
#ifndef TKT_ROOT
#ifdef MSDOS_FILESYSTEM
#else
#endif
#endif
/* macros to make checking flags easier */
typedef struct _krb5_fcc_data {
char *filename;
/* Lock this one before reading or modifying the data stored here
that can be changed. (Filename is fixed after
initialization.) */
int file;
/* Buffer data on reading, for performance.
We used to have a stdio option, but we get more precise control
by using the POSIX I/O functions. */
int valid_bytes;
int cur_offset;
{
data->valid_bytes = 0;
}
{
/* If we read some extra data in advance, and then want to know or
use our "current" position, we need to back up a little. */
}
}
struct fcc_set {
unsigned int refcount;
};
/* An off_t can be arbitrarily complex */
typedef struct _krb5_fcc_cursor {
{ \
if (maybe_open_ret) { \
return maybe_open_ret; \
} \
} \
}
{ \
{ \
/*
* Effects:
* Reads len bytes from the cache id, storing them in buf.
*
* Requires:
* Must be called with mutex locked.
*
* Errors:
* KRB5_CC_END - there were not len bytes available
* system errors (read)
*/
static krb5_error_code
{
#if 0
int ret;
if (ret == -1)
return KRB5_CC_END;
else
return KRB5_OK;
#else
while (len > 0) {
int nread, e;
if (data->valid_bytes > 0)
if (data->valid_bytes == 0
/* Fill buffer from current file position. */
e = errno;
if (nread < 0)
return krb5_fcc_interpret(context, e);
if (nread == 0)
/* EOF */
return KRB5_CC_END;
data->cur_offset = 0;
}
/* Don't do arithmetic on void pointers. */
}
return 0;
#endif
}
/*
* FOR ALL OF THE FOLLOWING FUNCTIONS:
*
* Requires:
* id is open and set to read at the appropriate place in the file
*
* mutex is locked
*
* Effects:
* Fills in the second argument with data of the appropriate type from
* the file. In some cases, the functions have to allocate space for
* variable length fields; therefore, krb5_destroy_<type> must be
* called for each filled in structure.
*
* Errors:
* system errors (read errors)
* KRB5_CC_NOMEM
*/
static krb5_error_code
{
int i;
} else {
/* Read principal type */
return kret;
}
/* Read the number of components */
return kret;
/*
* DCE includes the principal's realm in the count; the new format
* does not.
*/
length--;
if (length < 0)
return KRB5_CC_NOMEM;
return KRB5_CC_NOMEM;
if (length) {
return KRB5_CC_NOMEM;
}
return KRB5_CC_NOMEM;
}
} else
i = 0;
for (i=0; i < length; i++) {
}
return KRB5_OK;
while(--i >= 0)
return kret;
}
static krb5_error_code
{
int i;
*addrs = 0;
/* Read the number of components */
/* Make *addrs able to hold length pointers to krb5_address structs
* Add one extra for a null-terminated list
*/
msize += 1;
return KRB5_CC_NOMEM;
return KRB5_CC_NOMEM;
for (i=0; i < length; i++) {
/* Solaris Kerberos */
return KRB5_CC_NOMEM;
}
}
return KRB5_OK;
if (*addrs) {
}
return kret;
}
static krb5_error_code
{
/* This works because the old etype is the same as the new enctype. */
/* keyblock->enctype = ui2; */
}
if (int32 < 0)
return KRB5_CC_NOMEM;
/* Overflow check. */
return KRB5_CC_NOMEM;
return KRB5_OK;
/* Solaris Kerberos */
return KRB5_CC_NOMEM;
if (kret)
goto errout;
return KRB5_OK;
}
return kret;
}
static krb5_error_code
{
if (len < 0)
return KRB5_CC_NOMEM;
return KRB5_CC_NOMEM;
return KRB5_OK;
}
return KRB5_CC_NOMEM;
return KRB5_OK;
}
return kret;
}
static krb5_error_code
{
return KRB5_CC_NOMEM;
/* Length field is "unsigned int", which may be smaller than 32
bits. */
return KRB5_CC_NOMEM; /* XXX */
return KRB5_OK;
return KRB5_CC_NOMEM;
return KRB5_OK;
}
return kret;
}
static krb5_error_code
{
else {
if (retval)
return retval;
*i = val;
return 0;
}
}
static krb5_error_code
{
else {
if (retval)
return retval;
return 0;
}
}
static krb5_error_code
{
}
static krb5_error_code
{
krb5_int32 i;
else {
t->authtime = i;
t->starttime = i;
t->endtime = i;
t->renew_till = i;
}
return 0;
return retval;
}
static krb5_error_code
{
int i;
*a = 0;
/* Read the number of components */
if (length == 0)
return KRB5_OK;
/* Make *a able to hold length pointers to krb5_authdata structs
* Add one extra for a null-terminated list
*/
msize += 1;
return KRB5_CC_NOMEM;
if (*a == NULL)
return KRB5_CC_NOMEM;
for (i=0; i < length; i++) {
if ((*a)[i] == NULL) {
krb5_free_authdata(context, *a);
return KRB5_CC_NOMEM;
}
}
return KRB5_OK;
if (*a) {
krb5_free_authdata(context, *a);
*a = NULL;
}
return kret;
}
static krb5_error_code
{
a->magic = KV5M_AUTHDATA;
return KRB5_CC_NOMEM;
/* Value could have gotten truncated if int is smaller than 32
bits. */
return KRB5_CC_NOMEM; /* XXX */
if (a->length == 0 )
return KRB5_OK;
return KRB5_CC_NOMEM;
return KRB5_OK;
if (a->contents) {
krb5_xfree(a->contents);
}
return kret;
}
/*
* Requires:
* id is open
*
* Effects:
* Writes len bytes from buf into the file cred cache id.
*
* Errors:
* system errors
*/
static krb5_error_code
{
int ret;
if (ret < 0)
return KRB5_CC_WRITE;
return KRB5_OK;
}
/*
* FOR ALL OF THE FOLLOWING FUNCTIONS:
*
* Requires:
* ((krb5_fcc_data *) id->data)->file is open and at the right position.
*
* mutex is locked
*
* Effects:
* Stores an encoded version of the second argument in the
* cache file.
*
* Errors:
* system errors
*/
static krb5_error_code
{
/*
* DCE-compatible format means that the length count
* includes the realm. (It also doesn't include the
* principal type information.)
*/
tmp++;
} else {
}
for (i=0; i < length; i++) {
}
return KRB5_OK;
}
static krb5_error_code
{
/* Count the number of components */
if (addrs) {
while (*temp++)
length += 1;
}
for (i=0; i < length; i++) {
}
return KRB5_OK;
}
static krb5_error_code
{
}
}
static krb5_error_code
{
}
static krb5_error_code
{
}
static krb5_error_code
{
else {
i >>= 8;
i >>= 8;
i >>= 8;
buf[0] = (unsigned char) (i & 0xFF);
}
}
static krb5_error_code
{
else {
i >>= 8;
i >>= 8;
i >>= 8;
buf[0] = (unsigned char) (i & 0xFF);
}
}
static krb5_error_code
{
} else {
i >>= 8;
buf[0] = (unsigned char) (i & 0xFF);
}
}
static krb5_error_code
{
ibuf = (krb5_octet) i;
}
static krb5_error_code
{
else {
return 0;
}
}
static krb5_error_code
{
if (a != NULL) {
length++;
}
for (i=0; i<length; i++) {
}
return KRB5_OK;
}
static krb5_error_code
{
}
static krb5_error_code
{
int ret;
return KRB5_FCC_INTERNAL;
if (retval)
return retval;
}
#if defined(ANSI_STDIO) || defined(_WIN32)
#else
#endif
#ifndef HAVE_SETVBUF
#endif
/* Solaris Kerberos */
static krb5_error_code
{
int error;
int fd;
int newfile = 0;
*ret_fd = -1;
/*
* Solaris Kerberos
* If we are opening in NOUNLINK mode, we have to check that the
* existing file, if any, is not a symlink. If it is, we try to
* delete and re-create it.
*/
return (-1);
}
return (-1);
}
}
if (fd == -1) {
if (fd != -1) {
newfile = 1;
} else {
/* If the file got created after the open we must retry */
return (0);
}
/*
* We failed since the file existed with wrong permissions.
* Let's try to unlink it and if that succeeds retry.
*/
filename);
return (-1);
}
return (0);
}
}
/* If we still don't have a valid fd, we stop trying */
if (fd == -1)
return (-1);
/*
* Solaris Kerberos
* If the file was not created now with a O_CREAT | O_EXCL open,
* we have opened an existing file. We should check if the file
* owner is us, if not, unlink and retry. If unlink fails we log
* the error and return.
*/
if (!newfile) {
return (-1);
}
/* Check if this is the same file we lstat'd earlier */
return (-1);
}
/*
* Solaris Kerberos
* Check if the cc filename uid matches owner of file.
* else skip this check.
*/
char *s = NULL;
/* make sure we have some non-null char after '_' */
if (!*++uidstr)
goto out;
/* make sure the uid part is all digits */
for (s = uidstr; *s; s++)
if (!isdigit(*s))
goto out;
"%s owned by %d instead of %d",
"could not unlink %s [%m]", filename);
return (-1);
}
return (0);
}
}
}
out:
return (0);
}
static krb5_error_code
{
int f, open_flag;
int lock_flag;
int retries;
int newfile = 0;
/* Don't know what state it's in; shut down and start anew. */
}
switch(mode) {
/* Solaris Kerberos */
break;
case FCC_OPEN_AND_ERASE:
break;
case FCC_OPEN_RDWR:
break;
case FCC_OPEN_RDONLY:
default:
break;
}
/*
* Solaris Kerberos
* If we are opening in NOUNLINK mode, check whether we are opening a
* symlink or a file owned by some other user and take preventive action.
*/
newfile = 0;
if (mode == FCC_OPEN_AND_ERASE_NOUNLINK) {
&f, &newfile);
if (retval == 0 && f == -1)
goto fcc_retry;
} else {
0600);
}
if (f == NO_FILE)
else
(void) close(f);
/* Solaris Kerberos wait some time before retrying */
goto fcc_retry;
}
return retval;
}
int cnt;
/*
* Solaris Kerberos
* If this file was not created, we have to flush existing data.
* This will happen only if we are doing an ERASE_NOUNLINK open.
*/
close(f);
}
/* write the version number */
sizeof(fcc_fvno)) {
goto done;
}
/* V4 of the credentials cache format allows for header tags */
fcc_flen = 0;
/* Write header length */
/* Write time offset tag */
}
}
goto done;
}
/* verify a valid version number is there */
goto done;
}
goto done;
}
{
goto done;
}
while (fcc_flen) {
{
goto done;
}
switch (fcc_tag) {
case FCC_TAG_DELTATIME:
goto done;
}
{
goto done;
}
break;
}
{
goto done;
}
break;
default:
goto done;
}
break;
}
}
}
done:
if (retval) {
(void) krb5_unlock_file(context, f);
(void) close(f);
}
return retval;
}
static krb5_error_code
{
return errno;
}
return KRB5_OK;
}
static krb5_error_code
{
return kret;
return KRB5_OK;
}
/*
* Modifies:
* id
*
* Effects:
* contents are destroyed.
*
* Errors:
* system errors
* permission errors
*/
static krb5_error_code KRB5_CALLCONV
{
int reti = 0;
if (kret)
return kret;
/*
* SUN14resync
* This is not needed and can cause problems with ktkt_warnd(1M)
* because it does tricks with getuid and if we enable this fchmod
* we get EPERM [file_owner] failures on fchmod.
*/
#if 0
#if defined(HAVE_FCHMOD) || defined(HAVE_CHMOD)
{
#ifdef HAVE_FCHMOD
#else
#endif
#endif
if (reti == -1) {
return kret;
}
}
#endif
return kret;
}
/*
* Drop the ref count; if it hits zero, remove the entry from the
* fcc_set list and free it.
*/
{
if (kerr)
return kerr;
break;
}
} else
return 0;
}
/*
* Modifies:
* id
*
* Effects:
* Closes the file cache, invalidates the id, and frees any resources
* associated with the cache.
*/
static krb5_error_code KRB5_CALLCONV
{
krb5_xfree(id);
return KRB5_OK;
}
/*
* Effects:
* Destroys the contents of id.
*
* Errors:
* system errors
*/
static krb5_error_code KRB5_CALLCONV
{
register int ret;
unsigned long i, size;
unsigned int wlen;
if (kret)
return kret;
if (ret < 0) {
goto cleanup;
}
}
else
#ifdef MSDOS_FILESYSTEM
/* "disgusting bit of UNIX trivia" - that's how the writers of NFS describe
** the ability of UNIX to still write to a file which has been unlinked.
** Naturally, the PC can't do this. As a result, we have to delete the file
** after we wipe it clean but that throws off all the error handling code.
** So we have do the work ourselves.
*/
if (ret == -1) {
size = 0; /* Nothing to wipe clean */
} else
while (size > 0) {
if (i < 0) {
/* Don't jump to cleanup--we still want to delete the file. */
break;
}
size -= i; /* We've read this much */
}
}
if (ret < 0) {
goto cleanup;
}
#else /* MSDOS_FILESYSTEM */
if (ret < 0) {
}
goto cleanup;
}
if (ret < 0) {
}
goto cleanup;
}
/* XXX This may not be legal XXX */
}
goto cleanup;
}
}
goto cleanup;
}
if (ret)
#endif /* MSDOS_FILESYSTEM */
krb5_xfree(id);
return kret;
}
extern const krb5_cc_ops krb5_fcc_ops;
/*
* Requires:
* residual is a legal path name, and a null-terminated string
*
* Modifies:
* id
*
* Effects:
* creates a file-based cred cache that will reside in the file
* residual. The cache is not opened, but the filename is reserved.
*
* Returns:
* A filled in krb5_ccache structure "id".
*
* Errors:
* KRB5_CC_NOMEM - there was insufficient memory to allocate the
* krb5_ccache. id is undefined.
* permission errors
*/
static krb5_error_code KRB5_CALLCONV
{
if (kret)
return kret;
break;
}
if (setptr) {
if (kret) {
return kret;
}
} else {
return KRB5_CC_NOMEM;
}
return KRB5_CC_NOMEM;
}
if (kret) {
return kret;
}
if (kret) {
return kret;
}
/* data->version,mode filled in for real later */
data->valid_bytes = 0;
return KRB5_CC_NOMEM;
}
}
return KRB5_CC_NOMEM;
}
/* other routines will get errors on open, and callers must expect them,
if cache is non-existent/unusable */
return KRB5_OK;
}
/*
* Effects:
* Prepares for a sequential search of the credentials cache.
* Returns and krb5_cc_cursor to be used with krb5_fcc_next_cred and
* krb5_fcc_end_seq_get.
*
* If the cache is modified between the time of this call and the time
* of the final krb5_fcc_end_seq_get, the results are undefined.
*
* Errors:
* KRB5_CC_NOMEM
* system errors
*/
static krb5_error_code KRB5_CALLCONV
{
if (kret)
return kret;
return KRB5_CC_NOMEM;
}
if (kret) {
return kret;
}
}
/* Make sure we start reading right after the primary principal */
if (kret) {
/* Solaris Kerberos - fix mem leak */
goto done;
}
if (kret) {
/* Solaris Kerberos - fix mem leak */
goto done;
}
done:
return kret;
}
/*
* Requires:
* cursor is a krb5_cc_cursor originally obtained from
* krb5_fcc_start_seq_get.
*
* Modifes:
* cursor, creds
*
* Effects:
* Fills in creds with the "next" credentals structure from the cache
* id. The actual order the creds are returned in is arbitrary.
* Space is allocated for the variable length fields in the
* credentials structure, so the object returned must be passed to
* krb5_destroy_credential.
*
* The cursor is updated for the next call to krb5_fcc_next_cred.
*
* Errors:
* system errors
*/
static krb5_error_code KRB5_CALLCONV
{
if (kret)
return kret;
if (kret) {
k5_mutex_unlock(&d->lock);
return kret;
}
lose:
k5_mutex_unlock(&d->lock);
return kret;
}
/*
* Requires:
* cursor is a krb5_cc_cursor originally obtained from
* krb5_fcc_start_seq_get.
*
* Modifies:
* id, cursor
*
* Effects:
* Finishes sequential processing of the file credentials ccache id,
* and invalidates the cursor (it must never be used after this call).
*/
/* ARGSUSED */
static krb5_error_code KRB5_CALLCONV
{
/* We don't do anything with the file cache itself, so
no need to lock anything. */
/* don't close; it may be left open by the caller,
MAYBE_CLOSE.
MAYBE_CLOSE(context, id, kret); */
return 0;
}
/*
* Effects:
* Creates a new file cred cache whose name is guaranteed to be
* unique. The name begins with the string TKT_ROOT (from fcc.h).
* The cache is not opened, but the new filename is reserved.
*
* Returns:
* The filled in krb5_ccache id.
*
* Errors:
* KRB5_CC_NOMEM - there was insufficient memory to allocate the
* krb5_ccache. id is undefined.
* system errors (from open)
*/
static krb5_error_code KRB5_CALLCONV
{
int ret;
NUL */
/* Set master lock */
if (kret)
return kret;
#ifdef HAVE_MKSTEMP
if (ret == -1) {
}
#else /*HAVE_MKSTEMP*/
/* Make sure the file name is reserved */
if (ret == -1) {
}
#endif
/* Allocate memory */
return KRB5_CC_NOMEM;
}
return KRB5_CC_NOMEM;
}
if (kret) {
return kret;
}
if (kret) {
return kret;
}
/*
* The file is initially closed at the end of this call...
*/
data->valid_bytes = 0;
/* data->version,mode filled in for real later */
/* Ignore user's umask, set mode = 0600 */
#ifndef HAVE_FCHMOD
#ifdef HAVE_CHMOD
#endif
#else
#endif
!= sizeof(fcc_fvno)) {
goto err_out;
}
/* For version 4 we save a length for the rest of the header */
!= sizeof(fcc_flen)) {
goto err_out;
}
}
goto err_out;
}
return KRB5_CC_NOMEM;
}
return KRB5_CC_NOMEM;
}
will get as to state confused */
return KRB5_OK;
return kret;
}
/*
* Requires:
* id is a file credential cache
*
* Returns:
* The name of the file cred cache id.
*/
static const char * KRB5_CALLCONV
{
}
/*
* Modifies:
* id, princ
*
* Effects:
* Retrieves the primary principal from id, as set with
* krb5_fcc_initialize. The principal is returned is allocated
* storage that must be freed by the caller via krb5_free_principal.
*
* Errors:
* system errors
* KRB5_CC_NOMEM
*/
static krb5_error_code KRB5_CALLCONV
{
if (kret)
return kret;
/* make sure we're beyond the header */
done:
return kret;
}
static krb5_error_code KRB5_CALLCONV
krb5_fcc_retrieve(krb5_context context, krb5_ccache id, krb5_flags whichfields, krb5_creds *mcreds, krb5_creds *creds)
{
}
/*
* Modifies:
* the file cache
*
* Effects:
* stores creds in the file cred cache
*
* Errors:
* system errors
* storage failure errors
*/
static krb5_error_code KRB5_CALLCONV
{
if (ret)
return ret;
/* Make sure we are writing to the end of the file */
/* Make sure we are writing to the end of the file */
if (ret < 0) {
}
lose:
return ret;
}
/*
* Non-functional stub implementation for krb5_fcc_remove
*
* Errors:
* KRB5_CC_NOSUPP - not implemented
*/
static krb5_error_code KRB5_CALLCONV
{
return KRB5_CC_NOSUPP;
}
/*
* Requires:
* id is a cred cache returned by krb5_fcc_resolve or
* krb5_fcc_generate_new, but has not been opened by krb5_fcc_initialize.
*
* Modifies:
* id
*
* Effects:
* Sets the operational flags of id to flags.
*/
static krb5_error_code KRB5_CALLCONV
{
if (ret)
return ret;
/* XXX This should check for illegal combinations, if any.. */
if (flags & KRB5_TC_OPENCLOSE) {
/* asking to turn on OPENCLOSE mode */
/* XXX Is this test necessary? */
} else {
/* asking to turn off OPENCLOSE mode, meaning it must be
left open. We open if it's not yet open */
}
return ret;
}
/*
* Requires:
* id is a cred cache returned by krb5_fcc_resolve or
* krb5_fcc_generate_new, but has not been opened by krb5_fcc_initialize.
*
* Modifies:
* id (mutex only; temporary)
*
* Effects:
* Returns the operational flags of id.
*/
static krb5_error_code KRB5_CALLCONV
{
if (ret)
return ret;
return ret;
}
static krb5_error_code
{
switch (errnum) {
case ENOENT:
break;
case EPERM:
case EACCES:
#ifdef EISDIR
case EISDIR: /* Mac doesn't have EISDIR */
#endif
case ENOTDIR:
#ifdef ELOOP
case ELOOP: /* Bad symlink is like no file. */
#endif
#ifdef ETXTBSY
case ETXTBSY:
#endif
case EBUSY:
case EROFS:
break;
case EINVAL:
case EEXIST: /* XXX */
case EFAULT:
case EBADF:
#ifdef ENAMETOOLONG
case ENAMETOOLONG:
#endif
#ifdef EWOULDBLOCK
case EWOULDBLOCK:
#endif
break;
#ifdef EDQUOT
case EDQUOT:
#endif
case ENOSPC:
case EIO:
case ENFILE:
case EMFILE:
case ENXIO:
default:
"Credentials cache I/O operation failed (%s)"),
}
return retval;
}
0,
"FILE",
};
#if defined(_WIN32)
/*
* krb5_change_cache should be called after the cache changes.
* A notification message is is posted out to all top level
* windows so that they may recheck the cache based on the
* changes made. We register a unique message type with which
* we'll communicate to all other processes.
*/
krb5_change_cache (void) {
return 0;
}
unsigned int KRB5_CALLCONV
krb5_get_notification_message (void) {
static unsigned int message = 0;
if (message == 0)
return message;
}
#else /* _WIN32 */
krb5_change_cache (void)
{
return 0;
}
unsigned int
{
return 0;
}
#endif /* _WIN32 */
0,
"FILE",
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
};