2N/A/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2N/A/*
2N/A * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A/*
2N/A * lib/krb5/ccache/cc_file.c
2N/A *
2N/A * Copyright 1990,1991,1992,1993,1994,2000,2004,2007 Massachusetts Institute of Technology.
2N/A * All Rights Reserved.
2N/A *
2N/A * Original stdio support copyright 1995 by Cygnus Support.
2N/A *
2N/A * Export of this software from the United States of America may
2N/A * require a specific license from the United States Government.
2N/A * It is the responsibility of any person or organization contemplating
2N/A * export to obtain such a license before exporting.
2N/A *
2N/A * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
2N/A * distribute this software and its documentation for any purpose and
2N/A * without fee is hereby granted, provided that the above copyright
2N/A * notice appear in all copies and that both that copyright notice and
2N/A * this permission notice appear in supporting documentation, and that
2N/A * the name of M.I.T. not be used in advertising or publicity pertaining
2N/A * to distribution of the software without specific, written prior
2N/A * permission. Furthermore if you modify this software you must label
2N/A * your software as modified software and not distribute it in such a
2N/A * fashion that it might be confused with the original M.I.T. software.
2N/A * M.I.T. makes no representations about the suitability of
2N/A * this software for any purpose. It is provided "as is" without express
2N/A * or implied warranty.
2N/A *
2N/A *
2N/A * implementation of file-based credentials cache
2N/A */
2N/A
2N/A/*
2N/A If OPENCLOSE is defined, each of the functions opens and closes the
2N/A file whenever it needs to access it. Otherwise, the file is opened
2N/A once in initialize and closed once is close.
2N/A
2N/A This library depends on UNIX-like file descriptors, and UNIX-like
2N/A behavior from the functions: open, close, read, write, lseek.
2N/A
2N/A The quasi-BNF grammar for a credentials cache:
2N/A
2N/A file ::=
2N/A principal list-of-credentials
2N/A
2N/A credential ::=
2N/A client (principal)
2N/A server (principal)
2N/A keyblock (keyblock)
2N/A times (ticket_times)
2N/A is_skey (boolean)
2N/A ticket_flags (flags)
2N/A ticket (data)
2N/A second_ticket (data)
2N/A
2N/A principal ::=
2N/A number of components (int32)
2N/A component 1 (data)
2N/A component 2 (data)
2N/A ...
2N/A
2N/A data ::=
2N/A length (int32)
2N/A string of length bytes
2N/A
2N/A etc.
2N/A*/
2N/A/* todo:
2N/A Make sure that each time a function returns KRB5_NOMEM, everything
2N/A allocated earlier in the function and stack tree is freed.
2N/A
2N/A File locking
2N/A
2N/A Use pread/pwrite if available, so multiple threads can read
2N/A simultaneously. (That may require reader/writer locks.)
2N/A
2N/A fcc_nseq.c and fcc_read don't check return values a lot.
2N/A*/
2N/A#include "k5-int.h"
2N/A#include "cc-int.h"
2N/A
2N/A/* Solaris Kerberos */
2N/A#include <syslog.h>
2N/A#include <locale.h>
2N/A#include <ctype.h>
2N/A
2N/A#include <stdio.h>
2N/A#include <errno.h>
2N/A
2N/A#if HAVE_UNISTD_H
2N/A#include <unistd.h>
2N/A#endif
2N/A
2N/A/* How long to block if flock fails with EAGAIN */
2N/A#define LOCK_RETRIES 100
2N/A#define WAIT_LENGTH 20 /* in milliseconds */
2N/A
2N/A#ifdef HAVE_NETINET_IN_H
2N/A#if !defined(_WIN32)
2N/A#include <netinet/in.h>
2N/A#else
2N/A#include "port-sockets.h"
2N/A#endif
2N/A#else
2N/A# error find some way to use net-byte-order file version numbers.
2N/A#endif
2N/A
2N/Astatic krb5_error_code KRB5_CALLCONV krb5_fcc_close
2N/A(krb5_context, krb5_ccache id);
2N/A
2N/Astatic krb5_error_code KRB5_CALLCONV krb5_fcc_destroy
2N/A(krb5_context, krb5_ccache id);
2N/A
2N/Astatic krb5_error_code KRB5_CALLCONV krb5_fcc_end_seq_get
2N/A(krb5_context, krb5_ccache id, krb5_cc_cursor *cursor);
2N/A
2N/Astatic krb5_error_code KRB5_CALLCONV krb5_fcc_generate_new
2N/A(krb5_context, krb5_ccache *id);
2N/A
2N/Astatic const char * KRB5_CALLCONV krb5_fcc_get_name
2N/A(krb5_context, krb5_ccache id);
2N/A
2N/Astatic krb5_error_code KRB5_CALLCONV krb5_fcc_get_principal
2N/A(krb5_context, krb5_ccache id, krb5_principal *princ);
2N/A
2N/Astatic krb5_error_code KRB5_CALLCONV krb5_fcc_initialize
2N/A(krb5_context, krb5_ccache id, krb5_principal princ);
2N/A
2N/Astatic krb5_error_code KRB5_CALLCONV krb5_fcc_next_cred
2N/A(krb5_context, krb5_ccache id, krb5_cc_cursor *cursor,
2N/A krb5_creds *creds);
2N/A
2N/Astatic krb5_error_code krb5_fcc_read
2N/A(krb5_context, krb5_ccache id, krb5_pointer buf, unsigned int len);
2N/Astatic krb5_error_code krb5_fcc_read_principal
2N/A(krb5_context, krb5_ccache id, krb5_principal *princ);
2N/Astatic krb5_error_code krb5_fcc_read_keyblock
2N/A(krb5_context, krb5_ccache id, krb5_keyblock *keyblock);
2N/Astatic krb5_error_code krb5_fcc_read_data
2N/A(krb5_context, krb5_ccache id, krb5_data *data);
2N/Astatic krb5_error_code krb5_fcc_read_int32
2N/A(krb5_context, krb5_ccache id, krb5_int32 *i);
2N/Astatic krb5_error_code krb5_fcc_read_ui_2
2N/A(krb5_context, krb5_ccache id, krb5_ui_2 *i);
2N/Astatic krb5_error_code krb5_fcc_read_octet
2N/A(krb5_context, krb5_ccache id, krb5_octet *i);
2N/Astatic krb5_error_code krb5_fcc_read_times
2N/A(krb5_context, krb5_ccache id, krb5_ticket_times *t);
2N/Astatic krb5_error_code krb5_fcc_read_addrs
2N/A(krb5_context, krb5_ccache, krb5_address ***);
2N/Astatic krb5_error_code krb5_fcc_read_addr
2N/A(krb5_context, krb5_ccache, krb5_address *);
2N/Astatic krb5_error_code krb5_fcc_read_authdata
2N/A(krb5_context, krb5_ccache, krb5_authdata ***);
2N/Astatic krb5_error_code krb5_fcc_read_authdatum
2N/A(krb5_context, krb5_ccache, krb5_authdata *);
2N/A
2N/Astatic krb5_error_code KRB5_CALLCONV krb5_fcc_resolve
2N/A(krb5_context, krb5_ccache *id, const char *residual);
2N/A
2N/Astatic krb5_error_code KRB5_CALLCONV krb5_fcc_retrieve
2N/A(krb5_context, krb5_ccache id, krb5_flags whichfields,
2N/A krb5_creds *mcreds, krb5_creds *creds);
2N/A
2N/Astatic krb5_error_code KRB5_CALLCONV krb5_fcc_start_seq_get
2N/A(krb5_context, krb5_ccache id, krb5_cc_cursor *cursor);
2N/A
2N/Astatic krb5_error_code KRB5_CALLCONV krb5_fcc_store
2N/A(krb5_context, krb5_ccache id, krb5_creds *creds);
2N/A
2N/Astatic krb5_error_code krb5_fcc_skip_header
2N/A(krb5_context, krb5_ccache);
2N/Astatic krb5_error_code krb5_fcc_skip_principal
2N/A(krb5_context, krb5_ccache id);
2N/A
2N/Astatic krb5_error_code KRB5_CALLCONV krb5_fcc_set_flags
2N/A(krb5_context, krb5_ccache id, krb5_flags flags);
2N/A
2N/Astatic krb5_error_code KRB5_CALLCONV krb5_fcc_ptcursor_new
2N/A(krb5_context context, krb5_cc_ptcursor *cursor);
2N/A
2N/Astatic krb5_error_code KRB5_CALLCONV krb5_fcc_ptcursor_next
2N/A(krb5_context context, krb5_cc_ptcursor cursor, krb5_ccache *ccache);
2N/A
2N/Astatic krb5_error_code KRB5_CALLCONV krb5_fcc_ptcursor_free
2N/A(krb5_context context, krb5_cc_ptcursor *cursor);
2N/A
2N/Astatic krb5_error_code KRB5_CALLCONV krb5_fcc_last_change_time
2N/A(krb5_context context, krb5_ccache id, krb5_timestamp *change_time);
2N/A
2N/Astatic krb5_error_code KRB5_CALLCONV krb5_fcc_lock
2N/A(krb5_context context, krb5_ccache id);
2N/A
2N/Astatic krb5_error_code KRB5_CALLCONV krb5_fcc_unlock
2N/A(krb5_context context, krb5_ccache id);
2N/A
2N/A
2N/Aextern const krb5_cc_ops krb5_cc_file_ops;
2N/A
2N/Akrb5_error_code krb5_change_cache (void);
2N/A
2N/Astatic krb5_error_code krb5_fcc_write
2N/A(krb5_context, krb5_ccache id, krb5_pointer buf, unsigned int len);
2N/Astatic krb5_error_code krb5_fcc_store_principal
2N/A(krb5_context, krb5_ccache id, krb5_principal princ);
2N/Astatic krb5_error_code krb5_fcc_store_keyblock
2N/A(krb5_context, krb5_ccache id, krb5_keyblock *keyblock);
2N/Astatic krb5_error_code krb5_fcc_store_data
2N/A(krb5_context, krb5_ccache id, krb5_data *data);
2N/Astatic krb5_error_code krb5_fcc_store_int32
2N/A(krb5_context, krb5_ccache id, krb5_int32 i);
2N/Astatic krb5_error_code krb5_fcc_store_ui_4
2N/A(krb5_context, krb5_ccache id, krb5_ui_4 i);
2N/Astatic krb5_error_code krb5_fcc_store_ui_2
2N/A(krb5_context, krb5_ccache id, krb5_int32 i);
2N/Astatic krb5_error_code krb5_fcc_store_octet
2N/A(krb5_context, krb5_ccache id, krb5_int32 i);
2N/Astatic krb5_error_code krb5_fcc_store_times
2N/A(krb5_context, krb5_ccache id, krb5_ticket_times *t);
2N/Astatic krb5_error_code krb5_fcc_store_addrs
2N/A(krb5_context, krb5_ccache, krb5_address **);
2N/Astatic krb5_error_code krb5_fcc_store_addr
2N/A(krb5_context, krb5_ccache, krb5_address *);
2N/Astatic krb5_error_code krb5_fcc_store_authdata
2N/A(krb5_context, krb5_ccache, krb5_authdata **);
2N/Astatic krb5_error_code krb5_fcc_store_authdatum
2N/A(krb5_context, krb5_ccache, krb5_authdata *);
2N/A
2N/Astatic krb5_error_code krb5_fcc_interpret
2N/A(krb5_context, int);
2N/A
2N/Astruct _krb5_fcc_data;
2N/Astatic krb5_error_code krb5_fcc_close_file
2N/A(krb5_context, struct _krb5_fcc_data *data);
2N/Astatic krb5_error_code krb5_fcc_open_file
2N/A(krb5_context, krb5_ccache, int);
2N/Astatic krb5_error_code krb5_fcc_data_last_change_time
2N/A(krb5_context context, struct _krb5_fcc_data *data,
2N/A krb5_timestamp *change_time);
2N/A
2N/A
2N/A#define KRB5_OK 0
2N/A
2N/A#define KRB5_FCC_MAXLEN 100
2N/A
2N/A/*
2N/A * FCC version 2 contains type information for principals. FCC
2N/A * version 1 does not.
2N/A *
2N/A * FCC version 3 contains keyblock encryption type information, and is
2N/A * architecture independent. Previous versions are not.
2N/A *
2N/A * The code will accept version 1, 2, and 3 ccaches, and depending
2N/A * what KRB5_FCC_DEFAULT_FVNO is set to, it will create version 1, 2,
2N/A * or 3 FCC caches.
2N/A *
2N/A * The default credentials cache should be type 3 for now (see
2N/A * init_ctx.c).
2N/A */
2N/A
2N/A#define KRB5_FCC_FVNO_1 0x0501 /* krb v5, fcc v1 */
2N/A#define KRB5_FCC_FVNO_2 0x0502 /* krb v5, fcc v2 */
2N/A#define KRB5_FCC_FVNO_3 0x0503 /* krb v5, fcc v3 */
2N/A#define KRB5_FCC_FVNO_4 0x0504 /* krb v5, fcc v4 */
2N/A
2N/A#define FCC_OPEN_AND_ERASE 1
2N/A#define FCC_OPEN_RDWR 2
2N/A#define FCC_OPEN_RDONLY 3
2N/A#define FCC_OPEN_AND_ERASE_NOUNLINK 255 /* Solaris Kerberos */
2N/A
2N/A/* Credential file header tags.
2N/A * The header tags are constructed as:
2N/A * krb5_ui_2 tag
2N/A * krb5_ui_2 len
2N/A * krb5_octet data[len]
2N/A * This format allows for older versions of the fcc processing code to skip
2N/A * past unrecognized tag formats.
2N/A */
2N/A#define FCC_TAG_DELTATIME 1
2N/A
2N/A#ifndef TKT_ROOT
2N/A#ifdef MSDOS_FILESYSTEM
2N/A#define TKT_ROOT "\\tkt"
2N/A#else
2N/A#define TKT_ROOT "/tmp/tkt"
2N/A#endif
2N/A#endif
2N/A
2N/A/* macros to make checking flags easier */
2N/A#define OPENCLOSE(id) (((krb5_fcc_data *)id->data)->flags & KRB5_TC_OPENCLOSE)
2N/A
2N/Atypedef struct _krb5_fcc_data {
2N/A char *filename;
2N/A /* Lock this one before reading or modifying the data stored here
2N/A that can be changed. (Filename is fixed after
2N/A initialization.) */
2N/A k5_cc_mutex lock;
2N/A int file;
2N/A krb5_flags flags;
2N/A int mode; /* needed for locking code */
2N/A int version; /* version number of the file */
2N/A
2N/A /* Buffer data on reading, for performance.
2N/A We used to have a stdio option, but we get more precise control
2N/A by using the POSIX I/O functions. */
2N/A#define FCC_BUFSIZ 1024
2N/A size_t valid_bytes;
2N/A size_t cur_offset;
2N/A char buf[FCC_BUFSIZ];
2N/A} krb5_fcc_data;
2N/A
2N/Astatic inline void invalidate_cache(krb5_fcc_data *data)
2N/A{
2N/A data->valid_bytes = 0;
2N/A}
2N/A
2N/Astatic off_t fcc_lseek(krb5_fcc_data *data, off_t offset, int whence)
2N/A{
2N/A /* If we read some extra data in advance, and then want to know or
2N/A use our "current" position, we need to back up a little. */
2N/A if (whence == SEEK_CUR && data->valid_bytes) {
2N/A assert(data->cur_offset > 0);
2N/A assert(data->cur_offset <= data->valid_bytes);
2N/A offset -= (data->valid_bytes - data->cur_offset);
2N/A }
2N/A invalidate_cache(data);
2N/A return lseek(data->file, offset, whence);
2N/A}
2N/A
2N/Astruct fcc_set {
2N/A struct fcc_set *next;
2N/A krb5_fcc_data *data;
2N/A unsigned int refcount;
2N/A};
2N/A
2N/Ak5_cc_mutex krb5int_cc_file_mutex = K5_CC_MUTEX_PARTIAL_INITIALIZER;
2N/Astatic struct fcc_set *fccs = NULL;
2N/A
2N/A/* Iterator over file caches. */
2N/Astruct krb5_fcc_ptcursor_data {
2N/A struct fcc_set *cur;
2N/A};
2N/A
2N/A/* An off_t can be arbitrarily complex */
2N/Atypedef struct _krb5_fcc_cursor {
2N/A off_t pos;
2N/A} krb5_fcc_cursor;
2N/A
2N/A#define MAYBE_OPEN(CONTEXT, ID, MODE) \
2N/A { \
2N/A k5_cc_mutex_assert_locked(CONTEXT, &((krb5_fcc_data *)(ID)->data)->lock); \
2N/A if (OPENCLOSE (ID)) { \
2N/A krb5_error_code maybe_open_ret; \
2N/A maybe_open_ret = krb5_fcc_open_file (CONTEXT,ID,MODE); \
2N/A if (maybe_open_ret) { \
2N/A k5_cc_mutex_unlock(CONTEXT, &((krb5_fcc_data *)(ID)->data)->lock); \
2N/A return maybe_open_ret; \
2N/A } \
2N/A } \
2N/A }
2N/A
2N/A#define MAYBE_CLOSE(CONTEXT, ID, RET) \
2N/A { \
2N/A if (OPENCLOSE (ID)) { \
2N/A krb5_error_code maybe_close_ret; \
2N/A maybe_close_ret = krb5_fcc_close_file (CONTEXT, \
2N/A (krb5_fcc_data *)(ID)->data); \
2N/A if (!(RET)) RET = maybe_close_ret; } }
2N/A
2N/A#define MAYBE_CLOSE_IGNORE(CONTEXT, ID) \
2N/A { \
2N/A if (OPENCLOSE (ID)) { \
2N/A (void) krb5_fcc_close_file (CONTEXT,(krb5_fcc_data *)(ID)->data); } }
2N/A
2N/A#define CHECK(ret) if (ret != KRB5_OK) goto errout;
2N/A
2N/A#define NO_FILE -1
2N/A
2N/A/*
2N/A * Effects:
2N/A * Reads len bytes from the cache id, storing them in buf.
2N/A *
2N/A * Requires:
2N/A * Must be called with mutex locked.
2N/A *
2N/A * Errors:
2N/A * KRB5_CC_END - there were not len bytes available
2N/A * system errors (read)
2N/A */
2N/Astatic krb5_error_code
2N/Akrb5_fcc_read(krb5_context context, krb5_ccache id, krb5_pointer buf, unsigned int len)
2N/A{
2N/A#if 0
2N/A int ret;
2N/A
2N/A k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
2N/A
2N/A ret = read(((krb5_fcc_data *) id->data)->file, (char *) buf, len);
2N/A if (ret == -1)
2N/A return krb5_fcc_interpret(context, errno);
2N/A if (ret != len)
2N/A return KRB5_CC_END;
2N/A else
2N/A return KRB5_OK;
2N/A#else
2N/A krb5_fcc_data *data = (krb5_fcc_data *) id->data;
2N/A
2N/A k5_cc_mutex_assert_locked(context, &data->lock);
2N/A
2N/A while (len > 0) {
2N/A int nread, e;
2N/A size_t ncopied;
2N/A
2N/A if (data->valid_bytes > 0)
2N/A assert(data->cur_offset <= data->valid_bytes);
2N/A if (data->valid_bytes == 0
2N/A || data->cur_offset == data->valid_bytes) {
2N/A /* Fill buffer from current file position. */
2N/A nread = read(data->file, data->buf, sizeof(data->buf));
2N/A e = errno;
2N/A if (nread < 0)
2N/A return krb5_fcc_interpret(context, e);
2N/A if (nread == 0)
2N/A /* EOF */
2N/A return KRB5_CC_END;
2N/A data->valid_bytes = nread;
2N/A data->cur_offset = 0;
2N/A }
2N/A assert(data->cur_offset < data->valid_bytes);
2N/A ncopied = len;
2N/A assert(ncopied == len);
2N/A if (data->valid_bytes - data->cur_offset < ncopied)
2N/A ncopied = data->valid_bytes - data->cur_offset;
2N/A memcpy(buf, data->buf + data->cur_offset, ncopied);
2N/A data->cur_offset += ncopied;
2N/A assert(data->cur_offset > 0);
2N/A assert(data->cur_offset <= data->valid_bytes);
2N/A len -= ncopied;
2N/A /* Don't do arithmetic on void pointers. */
2N/A buf = (char*)buf + ncopied;
2N/A }
2N/A return 0;
2N/A#endif
2N/A}
2N/A
2N/A/*
2N/A * FOR ALL OF THE FOLLOWING FUNCTIONS:
2N/A *
2N/A * Requires:
2N/A * id is open and set to read at the appropriate place in the file
2N/A *
2N/A * mutex is locked
2N/A *
2N/A * Effects:
2N/A * Fills in the second argument with data of the appropriate type from
2N/A * the file. In some cases, the functions have to allocate space for
2N/A * variable length fields; therefore, krb5_destroy_<type> must be
2N/A * called for each filled in structure.
2N/A *
2N/A * Errors:
2N/A * system errors (read errors)
2N/A * KRB5_CC_NOMEM
2N/A */
2N/A
2N/A#define ALLOC(NUM,TYPE) \
2N/A (((NUM) <= (((size_t)0-1)/ sizeof(TYPE))) \
2N/A ? (TYPE *) calloc((NUM), sizeof(TYPE)) \
2N/A : (errno = ENOMEM,(TYPE *) 0))
2N/A
2N/Astatic krb5_error_code
2N/Akrb5_fcc_read_principal(krb5_context context, krb5_ccache id, krb5_principal *princ)
2N/A{
2N/A krb5_fcc_data *data = (krb5_fcc_data *)id->data;
2N/A krb5_error_code kret;
2N/A register krb5_principal tmpprinc;
2N/A krb5_int32 length, type;
2N/A int i;
2N/A
2N/A k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
2N/A
2N/A *princ = NULL;
2N/A
2N/A if (data->version == KRB5_FCC_FVNO_1) {
2N/A type = KRB5_NT_UNKNOWN;
2N/A } else {
2N/A /* Read principal type */
2N/A kret = krb5_fcc_read_int32(context, id, &type);
2N/A if (kret != KRB5_OK)
2N/A return kret;
2N/A }
2N/A
2N/A /* Read the number of components */
2N/A kret = krb5_fcc_read_int32(context, id, &length);
2N/A if (kret != KRB5_OK)
2N/A return kret;
2N/A
2N/A /*
2N/A * DCE includes the principal's realm in the count; the new format
2N/A * does not.
2N/A */
2N/A if (data->version == KRB5_FCC_FVNO_1)
2N/A length--;
2N/A if (length < 0)
2N/A return KRB5_CC_NOMEM;
2N/A
2N/A tmpprinc = (krb5_principal) malloc(sizeof(krb5_principal_data));
2N/A if (tmpprinc == NULL)
2N/A return KRB5_CC_NOMEM;
2N/A if (length) {
2N/A size_t msize = length;
2N/A if (msize != (krb5_ui_4) length) {
2N/A free(tmpprinc);
2N/A return KRB5_CC_NOMEM;
2N/A }
2N/A tmpprinc->data = ALLOC (msize, krb5_data);
2N/A if (tmpprinc->data == 0) {
2N/A free(tmpprinc);
2N/A return KRB5_CC_NOMEM;
2N/A }
2N/A } else
2N/A tmpprinc->data = 0;
2N/A tmpprinc->magic = KV5M_PRINCIPAL;
2N/A tmpprinc->length = length;
2N/A tmpprinc->type = type;
2N/A
2N/A kret = krb5_fcc_read_data(context, id, krb5_princ_realm(context, tmpprinc));
2N/A
2N/A i = 0;
2N/A CHECK(kret);
2N/A
2N/A for (i=0; i < length; i++) {
2N/A kret = krb5_fcc_read_data(context, id, krb5_princ_component(context, tmpprinc, i));
2N/A CHECK(kret);
2N/A }
2N/A *princ = tmpprinc;
2N/A return KRB5_OK;
2N/A
2N/Aerrout:
2N/A while(--i >= 0)
2N/A free(krb5_princ_component(context, tmpprinc, i)->data);
2N/A free(krb5_princ_realm(context, tmpprinc)->data);
2N/A free(tmpprinc->data);
2N/A free(tmpprinc);
2N/A return kret;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Akrb5_fcc_read_addrs(krb5_context context, krb5_ccache id, krb5_address ***addrs)
2N/A{
2N/A krb5_error_code kret;
2N/A krb5_int32 length;
2N/A size_t msize;
2N/A int i;
2N/A
2N/A k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
2N/A
2N/A *addrs = 0;
2N/A
2N/A /* Read the number of components */
2N/A kret = krb5_fcc_read_int32(context, id, &length);
2N/A CHECK(kret);
2N/A
2N/A /* Make *addrs able to hold length pointers to krb5_address structs
2N/A * Add one extra for a null-terminated list
2N/A */
2N/A msize = length;
2N/A msize += 1;
2N/A if (msize == 0 || msize - 1 != (krb5_ui_4) length || length < 0)
2N/A return KRB5_CC_NOMEM;
2N/A *addrs = ALLOC (msize, krb5_address *);
2N/A if (*addrs == NULL)
2N/A return KRB5_CC_NOMEM;
2N/A
2N/A for (i=0; i < length; i++) {
2N/A (*addrs)[i] = (krb5_address *) malloc(sizeof(krb5_address));
2N/A if ((*addrs)[i] == NULL) {
2N/A krb5_free_addresses(context, *addrs);
2N/A *addrs = 0;
2N/A return KRB5_CC_NOMEM;
2N/A }
2N/A (*addrs)[i]->contents = NULL;
2N/A kret = krb5_fcc_read_addr(context, id, (*addrs)[i]);
2N/A CHECK(kret);
2N/A }
2N/A
2N/A return KRB5_OK;
2N/Aerrout:
2N/A if (*addrs) {
2N/A krb5_free_addresses(context, *addrs);
2N/A *addrs = NULL;
2N/A }
2N/A return kret;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Akrb5_fcc_read_keyblock(krb5_context context, krb5_ccache id, krb5_keyblock *keyblock)
2N/A{
2N/A krb5_fcc_data *data = (krb5_fcc_data *)id->data;
2N/A krb5_error_code kret;
2N/A krb5_ui_2 ui2;
2N/A krb5_int32 int32;
2N/A
2N/A k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
2N/A
2N/A keyblock->magic = KV5M_KEYBLOCK;
2N/A keyblock->contents = 0;
2N/A
2N/A kret = krb5_fcc_read_ui_2(context, id, &ui2);
2N/A keyblock->enctype = ui2;
2N/A CHECK(kret);
2N/A if (data->version == KRB5_FCC_FVNO_3) {
2N/A /* This works because the old etype is the same as the new enctype. */
2N/A kret = krb5_fcc_read_ui_2(context, id, &ui2);
2N/A /* keyblock->enctype = ui2; */
2N/A CHECK(kret);
2N/A }
2N/A
2N/A kret = krb5_fcc_read_int32(context, id, &int32);
2N/A CHECK(kret);
2N/A if (int32 < 0)
2N/A return KRB5_CC_NOMEM;
2N/A keyblock->length = int32;
2N/A /* Overflow check. */
2N/A if (keyblock->length != (krb5_ui_4) int32)
2N/A return KRB5_CC_NOMEM;
2N/A if ( keyblock->length == 0 )
2N/A return KRB5_OK;
2N/A /* Solaris Kerberos */
2N/A keyblock->contents = calloc(keyblock->length, sizeof(krb5_octet));
2N/A if (keyblock->contents == NULL)
2N/A return KRB5_CC_NOMEM;
2N/A
2N/A kret = krb5_fcc_read(context, id, keyblock->contents, keyblock->length);
2N/A if (kret)
2N/A goto errout;
2N/A
2N/A return KRB5_OK;
2N/Aerrout:
2N/A if (keyblock->contents) {
2N/A free(keyblock->contents);
2N/A keyblock->contents = NULL;
2N/A }
2N/A return kret;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Akrb5_fcc_read_data(krb5_context context, krb5_ccache id, krb5_data *data)
2N/A{
2N/A krb5_error_code kret;
2N/A krb5_int32 len;
2N/A
2N/A k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
2N/A
2N/A data->magic = KV5M_DATA;
2N/A data->data = 0;
2N/A
2N/A kret = krb5_fcc_read_int32(context, id, &len);
2N/A CHECK(kret);
2N/A if (len < 0)
2N/A return KRB5_CC_NOMEM;
2N/A data->length = len;
2N/A if (data->length != (krb5_ui_4) len || data->length + 1 == 0)
2N/A return KRB5_CC_NOMEM;
2N/A
2N/A if (data->length == 0) {
2N/A data->data = 0;
2N/A return KRB5_OK;
2N/A }
2N/A
2N/A data->data = (char *) malloc(data->length+1);
2N/A if (data->data == NULL)
2N/A return KRB5_CC_NOMEM;
2N/A
2N/A kret = krb5_fcc_read(context, id, data->data, (unsigned) data->length);
2N/A CHECK(kret);
2N/A
2N/A data->data[data->length] = 0; /* Null terminate, just in case.... */
2N/A return KRB5_OK;
2N/Aerrout:
2N/A if (data->data) {
2N/A free(data->data);
2N/A data->data = NULL;
2N/A }
2N/A return kret;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Akrb5_fcc_read_addr(krb5_context context, krb5_ccache id, krb5_address *addr)
2N/A{
2N/A krb5_error_code kret;
2N/A krb5_ui_2 ui2;
2N/A krb5_int32 int32;
2N/A
2N/A k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
2N/A
2N/A addr->magic = KV5M_ADDRESS;
2N/A addr->contents = 0;
2N/A
2N/A kret = krb5_fcc_read_ui_2(context, id, &ui2);
2N/A CHECK(kret);
2N/A addr->addrtype = ui2;
2N/A
2N/A kret = krb5_fcc_read_int32(context, id, &int32);
2N/A CHECK(kret);
2N/A if ((int32 & VALID_INT_BITS) != int32) /* Overflow int??? */
2N/A return KRB5_CC_NOMEM;
2N/A addr->length = int32;
2N/A /* Length field is "unsigned int", which may be smaller than 32
2N/A bits. */
2N/A if (addr->length != (krb5_ui_4) int32)
2N/A return KRB5_CC_NOMEM; /* XXX */
2N/A
2N/A if (addr->length == 0)
2N/A return KRB5_OK;
2N/A
2N/A addr->contents = (krb5_octet *) malloc(addr->length);
2N/A if (addr->contents == NULL)
2N/A return KRB5_CC_NOMEM;
2N/A
2N/A kret = krb5_fcc_read(context, id, addr->contents, addr->length);
2N/A CHECK(kret);
2N/A
2N/A return KRB5_OK;
2N/Aerrout:
2N/A if (addr->contents) {
2N/A free(addr->contents);
2N/A addr->contents = NULL;
2N/A }
2N/A return kret;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Akrb5_fcc_read_int32(krb5_context context, krb5_ccache id, krb5_int32 *i)
2N/A{
2N/A krb5_fcc_data *data = (krb5_fcc_data *)id->data;
2N/A krb5_error_code retval;
2N/A unsigned char buf[4];
2N/A
2N/A k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
2N/A
2N/A if ((data->version == KRB5_FCC_FVNO_1) ||
2N/A (data->version == KRB5_FCC_FVNO_2))
2N/A return krb5_fcc_read(context, id, (krb5_pointer) i, sizeof(krb5_int32));
2N/A else {
2N/A retval = krb5_fcc_read(context, id, buf, 4);
2N/A if (retval)
2N/A return retval;
2N/A *i = load_32_be (buf);
2N/A return 0;
2N/A }
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Akrb5_fcc_read_ui_2(krb5_context context, krb5_ccache id, krb5_ui_2 *i)
2N/A{
2N/A krb5_fcc_data *data = (krb5_fcc_data *)id->data;
2N/A krb5_error_code retval;
2N/A unsigned char buf[2];
2N/A
2N/A k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
2N/A
2N/A if ((data->version == KRB5_FCC_FVNO_1) ||
2N/A (data->version == KRB5_FCC_FVNO_2))
2N/A return krb5_fcc_read(context, id, (krb5_pointer) i, sizeof(krb5_ui_2));
2N/A else {
2N/A retval = krb5_fcc_read(context, id, buf, 2);
2N/A if (retval)
2N/A return retval;
2N/A *i = load_16_be (buf);
2N/A return 0;
2N/A }
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Akrb5_fcc_read_octet(krb5_context context, krb5_ccache id, krb5_octet *i)
2N/A{
2N/A k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
2N/A return krb5_fcc_read(context, id, (krb5_pointer) i, 1);
2N/A}
2N/A
2N/A
2N/Astatic krb5_error_code
2N/Akrb5_fcc_read_times(krb5_context context, krb5_ccache id, krb5_ticket_times *t)
2N/A{
2N/A krb5_fcc_data *data = (krb5_fcc_data *)id->data;
2N/A krb5_error_code retval;
2N/A krb5_int32 i;
2N/A
2N/A k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
2N/A
2N/A if ((data->version == KRB5_FCC_FVNO_1) ||
2N/A (data->version == KRB5_FCC_FVNO_2))
2N/A return krb5_fcc_read(context, id, (krb5_pointer) t, sizeof(krb5_ticket_times));
2N/A else {
2N/A retval = krb5_fcc_read_int32(context, id, &i);
2N/A CHECK(retval);
2N/A t->authtime = i;
2N/A
2N/A retval = krb5_fcc_read_int32(context, id, &i);
2N/A CHECK(retval);
2N/A t->starttime = i;
2N/A
2N/A retval = krb5_fcc_read_int32(context, id, &i);
2N/A CHECK(retval);
2N/A t->endtime = i;
2N/A
2N/A retval = krb5_fcc_read_int32(context, id, &i);
2N/A CHECK(retval);
2N/A t->renew_till = i;
2N/A }
2N/A return 0;
2N/Aerrout:
2N/A return retval;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Akrb5_fcc_read_authdata(krb5_context context, krb5_ccache id, krb5_authdata ***a)
2N/A{
2N/A krb5_error_code kret;
2N/A krb5_int32 length;
2N/A size_t msize;
2N/A int i;
2N/A
2N/A k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
2N/A
2N/A *a = 0;
2N/A
2N/A /* Read the number of components */
2N/A kret = krb5_fcc_read_int32(context, id, &length);
2N/A CHECK(kret);
2N/A
2N/A if (length == 0)
2N/A return KRB5_OK;
2N/A
2N/A /* Make *a able to hold length pointers to krb5_authdata structs
2N/A * Add one extra for a null-terminated list
2N/A */
2N/A msize = length;
2N/A msize += 1;
2N/A if (msize == 0 || msize - 1 != (krb5_ui_4) length || length < 0)
2N/A return KRB5_CC_NOMEM;
2N/A *a = ALLOC (msize, krb5_authdata *);
2N/A if (*a == NULL)
2N/A return KRB5_CC_NOMEM;
2N/A
2N/A for (i=0; i < length; i++) {
2N/A (*a)[i] = (krb5_authdata *) malloc(sizeof(krb5_authdata));
2N/A if ((*a)[i] == NULL) {
2N/A krb5_free_authdata(context, *a);
2N/A *a = NULL;
2N/A return KRB5_CC_NOMEM;
2N/A }
2N/A (*a)[i]->contents = NULL;
2N/A kret = krb5_fcc_read_authdatum(context, id, (*a)[i]);
2N/A CHECK(kret);
2N/A }
2N/A
2N/A return KRB5_OK;
2N/Aerrout:
2N/A if (*a) {
2N/A krb5_free_authdata(context, *a);
2N/A *a = NULL;
2N/A }
2N/A return kret;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Akrb5_fcc_read_authdatum(krb5_context context, krb5_ccache id, krb5_authdata *a)
2N/A{
2N/A krb5_error_code kret;
2N/A krb5_int32 int32;
2N/A krb5_int16 ui2; /* negative authorization data types are allowed */
2N/A
2N/A k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
2N/A
2N/A a->magic = KV5M_AUTHDATA;
2N/A a->contents = NULL;
2N/A
2N/A kret = krb5_fcc_read_ui_2(context, id, (krb5_ui_2 *)&ui2);
2N/A CHECK(kret);
2N/A a->ad_type = (krb5_authdatatype)ui2;
2N/A kret = krb5_fcc_read_int32(context, id, &int32);
2N/A CHECK(kret);
2N/A if ((int32 & VALID_INT_BITS) != int32) /* Overflow int??? */
2N/A return KRB5_CC_NOMEM;
2N/A a->length = int32;
2N/A /* Value could have gotten truncated if int is smaller than 32
2N/A bits. */
2N/A if (a->length != (krb5_ui_4) int32)
2N/A return KRB5_CC_NOMEM; /* XXX */
2N/A
2N/A if (a->length == 0 )
2N/A return KRB5_OK;
2N/A
2N/A a->contents = (krb5_octet *) malloc(a->length);
2N/A if (a->contents == NULL)
2N/A return KRB5_CC_NOMEM;
2N/A
2N/A kret = krb5_fcc_read(context, id, a->contents, a->length);
2N/A CHECK(kret);
2N/A
2N/A return KRB5_OK;
2N/Aerrout:
2N/A if (a->contents) {
2N/A free(a->contents);
2N/A a->contents = NULL;
2N/A }
2N/A return kret;
2N/A
2N/A}
2N/A#undef CHECK
2N/A
2N/A#define CHECK(ret) if (ret != KRB5_OK) return ret;
2N/A
2N/A/*
2N/A * Requires:
2N/A * id is open
2N/A *
2N/A * Effects:
2N/A * Writes len bytes from buf into the file cred cache id.
2N/A *
2N/A * Errors:
2N/A * system errors
2N/A */
2N/Astatic krb5_error_code
2N/Akrb5_fcc_write(krb5_context context, krb5_ccache id, krb5_pointer buf, unsigned int len)
2N/A{
2N/A int ret;
2N/A
2N/A k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
2N/A invalidate_cache((krb5_fcc_data *) id->data);
2N/A
2N/A ret = write(((krb5_fcc_data *)id->data)->file, (char *) buf, len);
2N/A if (ret < 0)
2N/A return krb5_fcc_interpret(context, errno);
2N/A if ((unsigned int) ret != len)
2N/A return KRB5_CC_WRITE;
2N/A return KRB5_OK;
2N/A}
2N/A
2N/A/*
2N/A * FOR ALL OF THE FOLLOWING FUNCTIONS:
2N/A *
2N/A * Requires:
2N/A * ((krb5_fcc_data *) id->data)->file is open and at the right position.
2N/A *
2N/A * mutex is locked
2N/A *
2N/A * Effects:
2N/A * Stores an encoded version of the second argument in the
2N/A * cache file.
2N/A *
2N/A * Errors:
2N/A * system errors
2N/A */
2N/A
2N/Astatic krb5_error_code
2N/Akrb5_fcc_store_principal(krb5_context context, krb5_ccache id, krb5_principal princ)
2N/A{
2N/A krb5_fcc_data *data = (krb5_fcc_data *)id->data;
2N/A krb5_error_code ret;
2N/A krb5_int32 i, length, tmp, type;
2N/A
2N/A k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
2N/A
2N/A type = krb5_princ_type(context, princ);
2N/A tmp = length = krb5_princ_size(context, princ);
2N/A
2N/A if (data->version == KRB5_FCC_FVNO_1) {
2N/A /*
2N/A * DCE-compatible format means that the length count
2N/A * includes the realm. (It also doesn't include the
2N/A * principal type information.)
2N/A */
2N/A tmp++;
2N/A } else {
2N/A ret = krb5_fcc_store_int32(context, id, type);
2N/A CHECK(ret);
2N/A }
2N/A
2N/A ret = krb5_fcc_store_int32(context, id, tmp);
2N/A CHECK(ret);
2N/A
2N/A ret = krb5_fcc_store_data(context, id, krb5_princ_realm(context, princ));
2N/A CHECK(ret);
2N/A
2N/A for (i=0; i < length; i++) {
2N/A ret = krb5_fcc_store_data(context, id, krb5_princ_component(context, princ, i));
2N/A CHECK(ret);
2N/A }
2N/A
2N/A return KRB5_OK;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Akrb5_fcc_store_addrs(krb5_context context, krb5_ccache id, krb5_address **addrs)
2N/A{
2N/A krb5_error_code ret;
2N/A krb5_address **temp;
2N/A krb5_int32 i, length = 0;
2N/A
2N/A k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
2N/A
2N/A /* Count the number of components */
2N/A if (addrs) {
2N/A temp = addrs;
2N/A while (*temp++)
2N/A length += 1;
2N/A }
2N/A
2N/A ret = krb5_fcc_store_int32(context, id, length);
2N/A CHECK(ret);
2N/A for (i=0; i < length; i++) {
2N/A ret = krb5_fcc_store_addr(context, id, addrs[i]);
2N/A CHECK(ret);
2N/A }
2N/A
2N/A return KRB5_OK;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Akrb5_fcc_store_keyblock(krb5_context context, krb5_ccache id, krb5_keyblock *keyblock)
2N/A{
2N/A krb5_fcc_data *data = (krb5_fcc_data *)id->data;
2N/A krb5_error_code ret;
2N/A
2N/A k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
2N/A
2N/A ret = krb5_fcc_store_ui_2(context, id, keyblock->enctype);
2N/A CHECK(ret);
2N/A if (data->version == KRB5_FCC_FVNO_3) {
2N/A ret = krb5_fcc_store_ui_2(context, id, keyblock->enctype);
2N/A CHECK(ret);
2N/A }
2N/A ret = krb5_fcc_store_ui_4(context, id, keyblock->length);
2N/A CHECK(ret);
2N/A return krb5_fcc_write(context, id, (char *) keyblock->contents, keyblock->length);
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Akrb5_fcc_store_addr(krb5_context context, krb5_ccache id, krb5_address *addr)
2N/A{
2N/A krb5_error_code ret;
2N/A
2N/A k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
2N/A
2N/A ret = krb5_fcc_store_ui_2(context, id, addr->addrtype);
2N/A CHECK(ret);
2N/A ret = krb5_fcc_store_ui_4(context, id, addr->length);
2N/A CHECK(ret);
2N/A return krb5_fcc_write(context, id, (char *) addr->contents, addr->length);
2N/A}
2N/A
2N/A
2N/Astatic krb5_error_code
2N/Akrb5_fcc_store_data(krb5_context context, krb5_ccache id, krb5_data *data)
2N/A{
2N/A krb5_error_code ret;
2N/A
2N/A k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
2N/A
2N/A ret = krb5_fcc_store_ui_4(context, id, data->length);
2N/A CHECK(ret);
2N/A return krb5_fcc_write(context, id, data->data, data->length);
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Akrb5_fcc_store_int32(krb5_context context, krb5_ccache id, krb5_int32 i)
2N/A{
2N/A return krb5_fcc_store_ui_4(context, id, (krb5_ui_4) i);
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Akrb5_fcc_store_ui_4(krb5_context context, krb5_ccache id, krb5_ui_4 i)
2N/A{
2N/A krb5_fcc_data *data = (krb5_fcc_data *)id->data;
2N/A unsigned char buf[4];
2N/A
2N/A k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
2N/A
2N/A if ((data->version == KRB5_FCC_FVNO_1) ||
2N/A (data->version == KRB5_FCC_FVNO_2))
2N/A return krb5_fcc_write(context, id, (char *) &i, sizeof(krb5_int32));
2N/A else {
2N/A store_32_be (i, buf);
2N/A return krb5_fcc_write(context, id, buf, 4);
2N/A }
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Akrb5_fcc_store_ui_2(krb5_context context, krb5_ccache id, krb5_int32 i)
2N/A{
2N/A krb5_fcc_data *data = (krb5_fcc_data *)id->data;
2N/A krb5_ui_2 ibuf;
2N/A unsigned char buf[2];
2N/A
2N/A k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
2N/A
2N/A if ((data->version == KRB5_FCC_FVNO_1) ||
2N/A (data->version == KRB5_FCC_FVNO_2)) {
2N/A ibuf = (krb5_ui_2) i;
2N/A return krb5_fcc_write(context, id, (char *) &ibuf, sizeof(krb5_ui_2));
2N/A } else {
2N/A store_16_be (i, buf);
2N/A return krb5_fcc_write(context, id, buf, 2);
2N/A }
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Akrb5_fcc_store_octet(krb5_context context, krb5_ccache id, krb5_int32 i)
2N/A{
2N/A krb5_octet ibuf;
2N/A
2N/A k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
2N/A
2N/A ibuf = (krb5_octet) i;
2N/A return krb5_fcc_write(context, id, (char *) &ibuf, 1);
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Akrb5_fcc_store_times(krb5_context context, krb5_ccache id, krb5_ticket_times *t)
2N/A{
2N/A krb5_fcc_data *data = (krb5_fcc_data *)id->data;
2N/A krb5_error_code retval;
2N/A
2N/A k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
2N/A
2N/A if ((data->version == KRB5_FCC_FVNO_1) ||
2N/A (data->version == KRB5_FCC_FVNO_2))
2N/A return krb5_fcc_write(context, id, (char *) t, sizeof(krb5_ticket_times));
2N/A else {
2N/A retval = krb5_fcc_store_int32(context, id, t->authtime);
2N/A CHECK(retval);
2N/A retval = krb5_fcc_store_int32(context, id, t->starttime);
2N/A CHECK(retval);
2N/A retval = krb5_fcc_store_int32(context, id, t->endtime);
2N/A CHECK(retval);
2N/A retval = krb5_fcc_store_int32(context, id, t->renew_till);
2N/A CHECK(retval);
2N/A return 0;
2N/A }
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Akrb5_fcc_store_authdata(krb5_context context, krb5_ccache id, krb5_authdata **a)
2N/A{
2N/A krb5_error_code ret;
2N/A krb5_authdata **temp;
2N/A krb5_int32 i, length=0;
2N/A
2N/A k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
2N/A
2N/A if (a != NULL) {
2N/A for (temp=a; *temp; temp++)
2N/A length++;
2N/A }
2N/A
2N/A ret = krb5_fcc_store_int32(context, id, length);
2N/A CHECK(ret);
2N/A for (i=0; i<length; i++) {
2N/A ret = krb5_fcc_store_authdatum (context, id, a[i]);
2N/A CHECK(ret);
2N/A }
2N/A return KRB5_OK;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Akrb5_fcc_store_authdatum (krb5_context context, krb5_ccache id, krb5_authdata *a)
2N/A{
2N/A krb5_error_code ret;
2N/A
2N/A k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
2N/A
2N/A ret = krb5_fcc_store_ui_2(context, id, a->ad_type);
2N/A CHECK(ret);
2N/A ret = krb5_fcc_store_ui_4(context, id, a->length);
2N/A CHECK(ret);
2N/A return krb5_fcc_write(context, id, (krb5_pointer) a->contents, a->length);
2N/A}
2N/A#undef CHECK
2N/A
2N/Astatic krb5_error_code
2N/Akrb5_fcc_close_file (krb5_context context, krb5_fcc_data *data)
2N/A{
2N/A int ret;
2N/A krb5_error_code retval;
2N/A
2N/A k5_cc_mutex_assert_locked(context, &data->lock);
2N/A
2N/A if (data->file == NO_FILE)
2N/A return KRB5_FCC_INTERNAL;
2N/A
2N/A retval = krb5_unlock_file(context, data->file);
2N/A ret = close (data->file);
2N/A data->file = NO_FILE;
2N/A if (retval)
2N/A return retval;
2N/A
2N/A return ret ? krb5_fcc_interpret (context, errno) : 0;
2N/A}
2N/A
2N/A#if defined(ANSI_STDIO) || defined(_WIN32)
2N/A#define BINARY_MODE "b"
2N/A#else
2N/A#define BINARY_MODE ""
2N/A#endif
2N/A
2N/A#ifndef HAVE_SETVBUF
2N/A#undef setvbuf
2N/A#define setvbuf(FILE,BUF,MODE,SIZE) \
2N/A ((SIZE) < BUFSIZE ? (abort(),0) : setbuf(FILE, BUF))
2N/A#endif
2N/A
2N/A/* Solaris Kerberos */
2N/Astatic krb5_error_code
2N/Akrb5_fcc_open_nounlink(char *filename, int open_flag, int *ret_fd, int *new)
2N/A{
2N/A struct stat lres;
2N/A struct stat fres;
2N/A int error;
2N/A uid_t uid, euid;
2N/A int fd;
2N/A int newfile = 0;
2N/A
2N/A *ret_fd = -1;
2N/A /*
2N/A * Solaris Kerberos
2N/A * If we are opening in NOUNLINK mode, we have to check that the
2N/A * existing file, if any, is not a symlink. If it is, we try to
2N/A * delete and re-create it.
2N/A */
2N/A error = lstat(filename, &lres);
2N/A if (error == -1 && errno != ENOENT) {
2N/A syslog(LOG_ERR, "lstat failed for %s [%m]", filename);
2N/A return (-1);
2N/A }
2N/A
2N/A if (error == 0 && !S_ISREG(lres.st_mode)) {
2N/A syslog(LOG_WARNING, "%s is not a plain file!", filename);
2N/A syslog(LOG_WARNING, "trying to unlink %s", filename);
2N/A if (unlink(filename) != 0) {
2N/A syslog(LOG_ERR, "could not unlink %s [%m]", filename);
2N/A return (-1);
2N/A }
2N/A }
2N/A
2N/A fd = THREEPARAMOPEN(filename, open_flag | O_NONBLOCK | O_NOFOLLOW, 0600);
2N/A if (fd == -1) {
2N/A if (errno == ENOENT) {
2N/A fd = THREEPARAMOPEN(filename, open_flag | O_EXCL | O_CREAT,
2N/A 0600);
2N/A if (fd != -1) {
2N/A newfile = 1;
2N/A } else {
2N/A /* If the file got created after the open we must retry */
2N/A if (errno == EEXIST)
2N/A return (0);
2N/A }
2N/A } else if (errno == EACCES) {
2N/A /*
2N/A * We failed since the file existed with wrong permissions.
2N/A * Let's try to unlink it and if that succeeds retry.
2N/A */
2N/A syslog(LOG_WARNING, "Insufficient permissions on %s", filename);
2N/A syslog(LOG_WARNING, "trying to unlink %s", filename);
2N/A if (unlink(filename) != 0) {
2N/A syslog(LOG_ERR, "could not unlink %s [%m]", filename);
2N/A return (-1);
2N/A }
2N/A return (0);
2N/A }
2N/A }
2N/A /* If we still don't have a valid fd, we stop trying */
2N/A if (fd == -1)
2N/A return (-1);
2N/A
2N/A /*
2N/A * Solaris Kerberos
2N/A * If the file was not created now with a O_CREAT | O_EXCL open,
2N/A * we have opened an existing file. We should check if the file
2N/A * owner is us, if not, unlink and retry. If unlink fails we log
2N/A * the error and return.
2N/A */
2N/A if (!newfile) {
2N/A if (fstat(fd, &fres) == -1) {
2N/A syslog(LOG_ERR, "lstat failed for %s [%m]", filename);
2N/A close(fd);
2N/A return (-1);
2N/A }
2N/A /* Check if this is the same file we lstat'd earlier */
2N/A if (lres.st_dev != fres.st_dev || lres.st_ino != fres.st_ino) {
2N/A syslog(LOG_ERR, "%s changed between stat and open!", filename);
2N/A close(fd);
2N/A return (-1);
2N/A }
2N/A
2N/A /*
2N/A * Solaris Kerberos
2N/A * Check if the cc filename uid matches owner of file.
2N/A * Expects cc file to be in the form of /tmp/krb5cc_<uid>,
2N/A * else skip this check.
2N/A */
2N/A if (strncmp(filename, "/tmp/krb5cc_", strlen("/tmp/krb5cc_")) == 0) {
2N/A uid_t fname_uid;
2N/A char *uidstr = strchr(filename, '_');
2N/A char *s = NULL;
2N/A
2N/A /* make sure we have some non-null char after '_' */
2N/A if (!*++uidstr)
2N/A goto out;
2N/A
2N/A /* make sure the uid part is all digits */
2N/A for (s = uidstr; *s; s++)
2N/A if (!isdigit(*s))
2N/A goto out;
2N/A
2N/A fname_uid = (uid_t) atoi(uidstr);
2N/A if (fname_uid != fres.st_uid) {
2N/A close(fd);
2N/A syslog(LOG_WARNING, "%s owned by %d instead of %d",
2N/A filename, fres.st_uid, fname_uid);
2N/A syslog(LOG_WARNING, "trying to unlink %s", filename);
2N/A if (unlink(filename) != 0) {
2N/A syslog(LOG_ERR, "could not unlink %s [%m]", filename);
2N/A return (-1);
2N/A }
2N/A return (0);
2N/A }
2N/A }
2N/A }
2N/A
2N/Aout:
2N/A *new = newfile;
2N/A *ret_fd = fd;
2N/A return (0);
2N/A}
2N/A
2N/A
2N/Astatic krb5_error_code
2N/Akrb5_fcc_open_file (krb5_context context, krb5_ccache id, int mode)
2N/A{
2N/A krb5_os_context os_ctx = &context->os_context;
2N/A krb5_fcc_data *data = (krb5_fcc_data *)id->data;
2N/A krb5_ui_2 fcc_fvno;
2N/A krb5_ui_2 fcc_flen;
2N/A krb5_ui_2 fcc_tag;
2N/A krb5_ui_2 fcc_taglen;
2N/A int f, open_flag;
2N/A int lock_flag;
2N/A krb5_error_code retval = 0;
2N/A int retries;
2N/A int newfile = 0;
2N/A
2N/A k5_cc_mutex_assert_locked(context, &data->lock);
2N/A invalidate_cache(data);
2N/A
2N/A if (data->file != NO_FILE) {
2N/A /* Don't know what state it's in; shut down and start anew. */
2N/A (void) krb5_unlock_file(context, data->file);
2N/A (void) close (data->file);
2N/A data->file = NO_FILE;
2N/A }
2N/A
2N/A switch(mode) {
2N/A /* Solaris Kerberos */
2N/A case FCC_OPEN_AND_ERASE_NOUNLINK:
2N/A open_flag = O_RDWR;
2N/A break;
2N/A case FCC_OPEN_AND_ERASE:
2N/A unlink(data->filename);
2N/A open_flag = O_CREAT|O_EXCL|O_TRUNC|O_RDWR;
2N/A break;
2N/A case FCC_OPEN_RDWR:
2N/A open_flag = O_RDWR;
2N/A break;
2N/A case FCC_OPEN_RDONLY:
2N/A default:
2N/A open_flag = O_RDONLY;
2N/A break;
2N/A }
2N/A
2N/Afcc_retry:
2N/A /*
2N/A * Solaris Kerberos
2N/A * If we are opening in NOUNLINK mode, check whether we are opening a
2N/A * symlink or a file owned by some other user and take preventive action.
2N/A */
2N/A newfile = 0;
2N/A if (mode == FCC_OPEN_AND_ERASE_NOUNLINK) {
2N/A retval = krb5_fcc_open_nounlink(data->filename, open_flag,
2N/A &f, &newfile);
2N/A if (retval == 0 && f == -1)
2N/A goto fcc_retry;
2N/A } else {
2N/A f = THREEPARAMOPEN (data->filename, open_flag | O_BINARY | O_NOFOLLOW,
2N/A 0600);
2N/A }
2N/A if (f == NO_FILE)
2N/A return krb5_fcc_interpret (context, errno);
2N/A
2N/A set_cloexec_fd(f);
2N/A
2N/A data->mode = mode;
2N/A
2N/A if (data->mode == FCC_OPEN_RDONLY)
2N/A lock_flag = KRB5_LOCKMODE_SHARED;
2N/A else
2N/A lock_flag = KRB5_LOCKMODE_EXCLUSIVE;
2N/A if ((retval = krb5_lock_file(context, f, lock_flag))) {
2N/A (void) close(f);
2N/A if (retval == EAGAIN && retries++ < LOCK_RETRIES) {
2N/A /* Solaris Kerberos wait some time before retrying */
2N/A if (poll(NULL, 0, WAIT_LENGTH) == 0)
2N/A goto fcc_retry;
2N/A }
2N/A syslog(LOG_ERR, "Failed to lock %s [%m]", data->filename);
2N/A return retval;
2N/A }
2N/A
2N/A if (mode == FCC_OPEN_AND_ERASE || mode == FCC_OPEN_AND_ERASE_NOUNLINK) {
2N/A /* write the version number */
2N/A int cnt;
2N/A
2N/A /*
2N/A * Solaris Kerberos
2N/A * If this file was not created, we have to flush existing data.
2N/A * This will happen only if we are doing an ERASE_NOUNLINK open.
2N/A */
2N/A if (newfile == 0 && (ftruncate(f, 0) == -1)) {
2N/A syslog(LOG_ERR, "ftruncate failed for %s [%m]", data->filename);
2N/A close(f);
2N/A return (krb5_fcc_interpret(context, errno));
2N/A }
2N/A
2N/A fcc_fvno = htons(context->fcc_default_format);
2N/A data->version = context->fcc_default_format;
2N/A if ((cnt = write(f, (char *)&fcc_fvno, sizeof(fcc_fvno))) !=
2N/A sizeof(fcc_fvno)) {
2N/A retval = ((cnt == -1) ? krb5_fcc_interpret(context, errno) :
2N/A KRB5_CC_IO);
2N/A goto done;
2N/A }
2N/A data->file = f;
2N/A
2N/A if (data->version == KRB5_FCC_FVNO_4) {
2N/A /* V4 of the credentials cache format allows for header tags */
2N/A fcc_flen = 0;
2N/A
2N/A if (os_ctx->os_flags & KRB5_OS_TOFFSET_VALID)
2N/A fcc_flen += (2*sizeof(krb5_ui_2) + 2*sizeof(krb5_int32));
2N/A
2N/A /* Write header length */
2N/A retval = krb5_fcc_store_ui_2(context, id, (krb5_int32)fcc_flen);
2N/A if (retval) goto done;
2N/A
2N/A if (os_ctx->os_flags & KRB5_OS_TOFFSET_VALID) {
2N/A /* Write time offset tag */
2N/A fcc_tag = FCC_TAG_DELTATIME;
2N/A fcc_taglen = 2*sizeof(krb5_int32);
2N/A
2N/A retval = krb5_fcc_store_ui_2(context,id,(krb5_int32)fcc_tag);
2N/A if (retval) goto done;
2N/A retval = krb5_fcc_store_ui_2(context,id,(krb5_int32)fcc_taglen);
2N/A if (retval) goto done;
2N/A retval = krb5_fcc_store_int32(context,id,os_ctx->time_offset);
2N/A if (retval) goto done;
2N/A retval = krb5_fcc_store_int32(context,id,os_ctx->usec_offset);
2N/A if (retval) goto done;
2N/A }
2N/A }
2N/A invalidate_cache(data);
2N/A goto done;
2N/A }
2N/A
2N/A /* verify a valid version number is there */
2N/A invalidate_cache(data);
2N/A if (read(f, (char *)&fcc_fvno, sizeof(fcc_fvno)) != sizeof(fcc_fvno)) {
2N/A retval = KRB5_CC_FORMAT;
2N/A goto done;
2N/A }
2N/A data->version = ntohs(fcc_fvno);
2N/A if ((data->version != KRB5_FCC_FVNO_4) &&
2N/A (data->version != KRB5_FCC_FVNO_3) &&
2N/A (data->version != KRB5_FCC_FVNO_2) &&
2N/A (data->version != KRB5_FCC_FVNO_1)) {
2N/A retval = KRB5_CCACHE_BADVNO;
2N/A goto done;
2N/A }
2N/A
2N/A data->file = f;
2N/A
2N/A if (data->version == KRB5_FCC_FVNO_4) {
2N/A char buf[1024];
2N/A
2N/A if (krb5_fcc_read_ui_2(context, id, &fcc_flen) ||
2N/A (fcc_flen > sizeof(buf)))
2N/A {
2N/A retval = KRB5_CC_FORMAT;
2N/A goto done;
2N/A }
2N/A
2N/A while (fcc_flen) {
2N/A if ((fcc_flen < (2 * sizeof(krb5_ui_2))) ||
2N/A krb5_fcc_read_ui_2(context, id, &fcc_tag) ||
2N/A krb5_fcc_read_ui_2(context, id, &fcc_taglen) ||
2N/A (fcc_taglen > (fcc_flen - 2*sizeof(krb5_ui_2))))
2N/A {
2N/A retval = KRB5_CC_FORMAT;
2N/A goto done;
2N/A }
2N/A
2N/A switch (fcc_tag) {
2N/A case FCC_TAG_DELTATIME:
2N/A if (fcc_taglen != 2*sizeof(krb5_int32)) {
2N/A retval = KRB5_CC_FORMAT;
2N/A goto done;
2N/A }
2N/A if (!(context->library_options & KRB5_LIBOPT_SYNC_KDCTIME) ||
2N/A (os_ctx->os_flags & KRB5_OS_TOFFSET_VALID))
2N/A {
2N/A if (krb5_fcc_read(context, id, buf, fcc_taglen)) {
2N/A retval = KRB5_CC_FORMAT;
2N/A goto done;
2N/A }
2N/A break;
2N/A }
2N/A if (krb5_fcc_read_int32(context, id, &os_ctx->time_offset) ||
2N/A krb5_fcc_read_int32(context, id, &os_ctx->usec_offset))
2N/A {
2N/A retval = KRB5_CC_FORMAT;
2N/A goto done;
2N/A }
2N/A os_ctx->os_flags =
2N/A ((os_ctx->os_flags & ~KRB5_OS_TOFFSET_TIME) |
2N/A KRB5_OS_TOFFSET_VALID);
2N/A break;
2N/A default:
2N/A if (fcc_taglen && krb5_fcc_read(context,id,buf,fcc_taglen)) {
2N/A retval = KRB5_CC_FORMAT;
2N/A goto done;
2N/A }
2N/A break;
2N/A }
2N/A fcc_flen -= (2*sizeof(krb5_ui_2) + fcc_taglen);
2N/A }
2N/A }
2N/A
2N/Adone:
2N/A if (retval) {
2N/A data->file = -1;
2N/A (void) krb5_unlock_file(context, f);
2N/A (void) close(f);
2N/A }
2N/A return retval;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Akrb5_fcc_skip_header(krb5_context context, krb5_ccache id)
2N/A{
2N/A krb5_fcc_data *data = (krb5_fcc_data *)id->data;
2N/A krb5_error_code kret;
2N/A krb5_ui_2 fcc_flen;
2N/A
2N/A k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
2N/A
2N/A fcc_lseek(data, (off_t) sizeof(krb5_ui_2), SEEK_SET);
2N/A if (data->version == KRB5_FCC_FVNO_4) {
2N/A kret = krb5_fcc_read_ui_2(context, id, &fcc_flen);
2N/A if (kret) return kret;
2N/A if(fcc_lseek(data, (off_t) fcc_flen, SEEK_CUR) < 0)
2N/A return errno;
2N/A }
2N/A return KRB5_OK;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Akrb5_fcc_skip_principal(krb5_context context, krb5_ccache id)
2N/A{
2N/A krb5_error_code kret;
2N/A krb5_principal princ;
2N/A
2N/A k5_cc_mutex_assert_locked(context, &((krb5_fcc_data *) id->data)->lock);
2N/A
2N/A kret = krb5_fcc_read_principal(context, id, &princ);
2N/A if (kret != KRB5_OK)
2N/A return kret;
2N/A
2N/A krb5_free_principal(context, princ);
2N/A return KRB5_OK;
2N/A}
2N/A
2N/A
2N/A/*
2N/A * Modifies:
2N/A * id
2N/A *
2N/A * Effects:
2N/A * Creates/refreshes the file cred cache id. If the cache exists, its
2N/A * contents are destroyed.
2N/A *
2N/A * Errors:
2N/A * system errors
2N/A * permission errors
2N/A */
2N/Astatic krb5_error_code KRB5_CALLCONV
2N/Akrb5_fcc_initialize(krb5_context context, krb5_ccache id, krb5_principal princ)
2N/A{
2N/A krb5_error_code kret = 0;
2N/A int reti = 0;
2N/A
2N/A kret = k5_cc_mutex_lock(context, &((krb5_fcc_data *) id->data)->lock);
2N/A if (kret)
2N/A return kret;
2N/A
2N/A MAYBE_OPEN(context, id, FCC_OPEN_AND_ERASE_NOUNLINK); /* Solaris Kerberos */
2N/A
2N/A /*
2N/A * SUN14resync
2N/A * This is not needed and can cause problems with ktkt_warnd(1M)
2N/A * because it does tricks with getuid and if we enable this fchmod
2N/A * we get EPERM [file_owner] failures on fchmod.
2N/A */
2N/A#if 0
2N/A#if defined(HAVE_FCHMOD) || defined(HAVE_CHMOD)
2N/A {
2N/A#ifdef HAVE_FCHMOD
2N/A reti = fchmod(((krb5_fcc_data *) id->data)->file, S_IREAD | S_IWRITE);
2N/A#else
2N/A reti = chmod(((krb5_fcc_data *) id->data)->filename, S_IREAD | S_IWRITE);
2N/A#endif
2N/A#endif
2N/A if (reti == -1) {
2N/A kret = krb5_fcc_interpret(context, errno);
2N/A MAYBE_CLOSE(context, id, kret);
2N/A k5_cc_mutex_unlock(context, &((krb5_fcc_data *) id->data)->lock);
2N/A return kret;
2N/A }
2N/A }
2N/A#endif
2N/A kret = krb5_fcc_store_principal(context, id, princ);
2N/A
2N/A MAYBE_CLOSE(context, id, kret);
2N/A k5_cc_mutex_unlock(context, &((krb5_fcc_data *) id->data)->lock);
2N/A krb5_change_cache ();
2N/A return kret;
2N/A}
2N/A
2N/A/*
2N/A * Drop the ref count; if it hits zero, remove the entry from the
2N/A * fcc_set list and free it.
2N/A */
2N/Astatic krb5_error_code dereference(krb5_context context, krb5_fcc_data *data)
2N/A{
2N/A krb5_error_code kerr;
2N/A struct fcc_set **fccsp;
2N/A
2N/A kerr = k5_cc_mutex_lock(context, &krb5int_cc_file_mutex);
2N/A if (kerr)
2N/A return kerr;
2N/A for (fccsp = &fccs; *fccsp != NULL; fccsp = &(*fccsp)->next)
2N/A if ((*fccsp)->data == data)
2N/A break;
2N/A assert(*fccsp != NULL);
2N/A assert((*fccsp)->data == data);
2N/A (*fccsp)->refcount--;
2N/A if ((*fccsp)->refcount == 0) {
2N/A struct fcc_set *temp;
2N/A data = (*fccsp)->data;
2N/A temp = *fccsp;
2N/A *fccsp = (*fccsp)->next;
2N/A free(temp);
2N/A k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
2N/A k5_cc_mutex_assert_unlocked(context, &data->lock);
2N/A free(data->filename);
2N/A zap(data->buf, sizeof(data->buf));
2N/A if (data->file >= 0) {
2N/A kerr = k5_cc_mutex_lock(context, &data->lock);
2N/A if (kerr)
2N/A return kerr;
2N/A krb5_fcc_close_file(context, data);
2N/A k5_cc_mutex_unlock(context, &data->lock);
2N/A }
2N/A k5_cc_mutex_destroy(&data->lock);
2N/A free(data);
2N/A } else
2N/A k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
2N/A return 0;
2N/A}
2N/A
2N/A/*
2N/A * Modifies:
2N/A * id
2N/A *
2N/A * Effects:
2N/A * Closes the file cache, invalidates the id, and frees any resources
2N/A * associated with the cache.
2N/A */
2N/Astatic krb5_error_code KRB5_CALLCONV
2N/Akrb5_fcc_close(krb5_context context, krb5_ccache id)
2N/A{
2N/A dereference(context, (krb5_fcc_data *) id->data);
2N/A free(id);
2N/A return KRB5_OK;
2N/A}
2N/A
2N/A/*
2N/A * Effects:
2N/A * Destroys the contents of id.
2N/A *
2N/A * Errors:
2N/A * system errors
2N/A */
2N/Astatic krb5_error_code KRB5_CALLCONV
2N/Akrb5_fcc_destroy(krb5_context context, krb5_ccache id)
2N/A{
2N/A krb5_error_code kret = 0;
2N/A krb5_fcc_data *data = (krb5_fcc_data *) id->data;
2N/A register int ret;
2N/A
2N/A struct stat buf;
2N/A unsigned long i, size;
2N/A unsigned int wlen;
2N/A char zeros[BUFSIZ];
2N/A
2N/A kret = k5_cc_mutex_lock(context, &data->lock);
2N/A if (kret)
2N/A return kret;
2N/A
2N/A if (OPENCLOSE(id)) {
2N/A invalidate_cache(data);
2N/A ret = THREEPARAMOPEN(data->filename,
2N/A O_RDWR | O_BINARY, 0);
2N/A if (ret < 0) {
2N/A kret = krb5_fcc_interpret(context, errno);
2N/A goto cleanup;
2N/A }
2N/A set_cloexec_fd(ret);
2N/A data->file = ret;
2N/A }
2N/A else
2N/A fcc_lseek(data, (off_t) 0, SEEK_SET);
2N/A
2N/A#ifdef MSDOS_FILESYSTEM
2N/A/* "disgusting bit of UNIX trivia" - that's how the writers of NFS describe
2N/A** the ability of UNIX to still write to a file which has been unlinked.
2N/A** Naturally, the PC can't do this. As a result, we have to delete the file
2N/A** after we wipe it clean but that throws off all the error handling code.
2N/A** So we have do the work ourselves.
2N/A*/
2N/A ret = fstat(data->file, &buf);
2N/A if (ret == -1) {
2N/A kret = krb5_fcc_interpret(context, errno);
2N/A size = 0; /* Nothing to wipe clean */
2N/A } else
2N/A size = (unsigned long) buf.st_size;
2N/A
2N/A memset(zeros, 0, BUFSIZ);
2N/A while (size > 0) {
2N/A wlen = (int) ((size > BUFSIZ) ? BUFSIZ : size); /* How much to write */
2N/A i = write(data->file, zeros, wlen);
2N/A if (i < 0) {
2N/A kret = krb5_fcc_interpret(context, errno);
2N/A /* Don't jump to cleanup--we still want to delete the file. */
2N/A break;
2N/A }
2N/A size -= i; /* We've read this much */
2N/A }
2N/A
2N/A if (OPENCLOSE(id)) {
2N/A (void) close(((krb5_fcc_data *)id->data)->file);
2N/A data->file = -1;
2N/A }
2N/A
2N/A ret = unlink(data->filename);
2N/A if (ret < 0) {
2N/A kret = krb5_fcc_interpret(context, errno);
2N/A goto cleanup;
2N/A }
2N/A
2N/A#else /* MSDOS_FILESYSTEM */
2N/A
2N/A ret = unlink(data->filename);
2N/A if (ret < 0) {
2N/A kret = krb5_fcc_interpret(context, errno);
2N/A if (OPENCLOSE(id)) {
2N/A (void) close(((krb5_fcc_data *)id->data)->file);
2N/A data->file = -1;
2N/A kret = ret;
2N/A }
2N/A goto cleanup;
2N/A }
2N/A
2N/A ret = fstat(data->file, &buf);
2N/A if (ret < 0) {
2N/A kret = krb5_fcc_interpret(context, errno);
2N/A if (OPENCLOSE(id)) {
2N/A (void) close(((krb5_fcc_data *)id->data)->file);
2N/A data->file = -1;
2N/A }
2N/A goto cleanup;
2N/A }
2N/A
2N/A /* XXX This may not be legal XXX */
2N/A size = (unsigned long) buf.st_size;
2N/A memset(zeros, 0, BUFSIZ);
2N/A for (i=0; i < size / BUFSIZ; i++)
2N/A if (write(data->file, zeros, BUFSIZ) < 0) {
2N/A kret = krb5_fcc_interpret(context, errno);
2N/A if (OPENCLOSE(id)) {
2N/A (void) close(((krb5_fcc_data *)id->data)->file);
2N/A data->file = -1;
2N/A }
2N/A goto cleanup;
2N/A }
2N/A
2N/A wlen = (unsigned int) (size % BUFSIZ);
2N/A if (write(data->file, zeros, wlen) < 0) {
2N/A kret = krb5_fcc_interpret(context, errno);
2N/A if (OPENCLOSE(id)) {
2N/A (void) close(((krb5_fcc_data *)id->data)->file);
2N/A data->file = -1;
2N/A }
2N/A goto cleanup;
2N/A }
2N/A
2N/A ret = close(data->file);
2N/A data->file = -1;
2N/A
2N/A if (ret)
2N/A kret = krb5_fcc_interpret(context, errno);
2N/A
2N/A#endif /* MSDOS_FILESYSTEM */
2N/A
2N/Acleanup:
2N/A k5_cc_mutex_unlock(context, &data->lock);
2N/A dereference(context, data);
2N/A free(id);
2N/A
2N/A krb5_change_cache ();
2N/A return kret;
2N/A}
2N/A
2N/Aextern const krb5_cc_ops krb5_fcc_ops;
2N/A
2N/A/*
2N/A * Requires:
2N/A * residual is a legal path name, and a null-terminated string
2N/A *
2N/A * Modifies:
2N/A * id
2N/A *
2N/A * Effects:
2N/A * creates a file-based cred cache that will reside in the file
2N/A * residual. The cache is not opened, but the filename is reserved.
2N/A *
2N/A * Returns:
2N/A * A filled in krb5_ccache structure "id".
2N/A *
2N/A * Errors:
2N/A * KRB5_CC_NOMEM - there was insufficient memory to allocate the
2N/A * krb5_ccache. id is undefined.
2N/A * permission errors
2N/A */
2N/Astatic krb5_error_code KRB5_CALLCONV
2N/Akrb5_fcc_resolve (krb5_context context, krb5_ccache *id, const char *residual)
2N/A{
2N/A krb5_ccache lid;
2N/A krb5_error_code kret;
2N/A krb5_fcc_data *data;
2N/A struct fcc_set *setptr;
2N/A
2N/A kret = k5_cc_mutex_lock(context, &krb5int_cc_file_mutex);
2N/A if (kret)
2N/A return kret;
2N/A for (setptr = fccs; setptr; setptr = setptr->next) {
2N/A if (!strcmp(setptr->data->filename, residual))
2N/A break;
2N/A }
2N/A if (setptr) {
2N/A data = setptr->data;
2N/A assert(setptr->refcount != 0);
2N/A setptr->refcount++;
2N/A assert(setptr->refcount != 0);
2N/A kret = k5_cc_mutex_lock(context, &data->lock);
2N/A if (kret) {
2N/A k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
2N/A return kret;
2N/A }
2N/A k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
2N/A } else {
2N/A data = malloc(sizeof(krb5_fcc_data));
2N/A if (data == NULL) {
2N/A k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
2N/A return KRB5_CC_NOMEM;
2N/A }
2N/A data->filename = strdup(residual);
2N/A if (data->filename == NULL) {
2N/A k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
2N/A free(data);
2N/A return KRB5_CC_NOMEM;
2N/A }
2N/A kret = k5_cc_mutex_init(&data->lock);
2N/A if (kret) {
2N/A k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
2N/A free(data->filename);
2N/A free(data);
2N/A return kret;
2N/A }
2N/A kret = k5_cc_mutex_lock(context, &data->lock);
2N/A if (kret) {
2N/A k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
2N/A k5_cc_mutex_destroy(&data->lock);
2N/A free(data->filename);
2N/A free(data);
2N/A return kret;
2N/A }
2N/A /* data->version,mode filled in for real later */
2N/A data->version = data->mode = 0;
2N/A data->flags = KRB5_TC_OPENCLOSE;
2N/A data->file = -1;
2N/A data->valid_bytes = 0;
2N/A setptr = malloc(sizeof(struct fcc_set));
2N/A if (setptr == NULL) {
2N/A k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
2N/A k5_cc_mutex_unlock(context, &data->lock);
2N/A k5_cc_mutex_destroy(&data->lock);
2N/A free(data->filename);
2N/A free(data);
2N/A return KRB5_CC_NOMEM;
2N/A }
2N/A setptr->refcount = 1;
2N/A setptr->data = data;
2N/A setptr->next = fccs;
2N/A fccs = setptr;
2N/A k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
2N/A }
2N/A
2N/A k5_cc_mutex_assert_locked(context, &data->lock);
2N/A k5_cc_mutex_unlock(context, &data->lock);
2N/A lid = (krb5_ccache) malloc(sizeof(struct _krb5_ccache));
2N/A if (lid == NULL) {
2N/A dereference(context, data);
2N/A return KRB5_CC_NOMEM;
2N/A }
2N/A
2N/A lid->ops = &krb5_fcc_ops;
2N/A lid->data = data;
2N/A lid->magic = KV5M_CCACHE;
2N/A
2N/A /* other routines will get errors on open, and callers must expect them,
2N/A if cache is non-existent/unusable */
2N/A *id = lid;
2N/A return KRB5_OK;
2N/A}
2N/A
2N/A/*
2N/A * Effects:
2N/A * Prepares for a sequential search of the credentials cache.
2N/A * Returns and krb5_cc_cursor to be used with krb5_fcc_next_cred and
2N/A * krb5_fcc_end_seq_get.
2N/A *
2N/A * If the cache is modified between the time of this call and the time
2N/A * of the final krb5_fcc_end_seq_get, the results are undefined.
2N/A *
2N/A * Errors:
2N/A * KRB5_CC_NOMEM
2N/A * system errors
2N/A */
2N/Astatic krb5_error_code KRB5_CALLCONV
2N/Akrb5_fcc_start_seq_get(krb5_context context, krb5_ccache id,
2N/A krb5_cc_cursor *cursor)
2N/A{
2N/A krb5_fcc_cursor *fcursor;
2N/A krb5_error_code kret = KRB5_OK;
2N/A krb5_fcc_data *data = (krb5_fcc_data *)id->data;
2N/A
2N/A kret = k5_cc_mutex_lock(context, &data->lock);
2N/A if (kret)
2N/A return kret;
2N/A
2N/A fcursor = (krb5_fcc_cursor *) malloc(sizeof(krb5_fcc_cursor));
2N/A if (fcursor == NULL) {
2N/A k5_cc_mutex_unlock(context, &data->lock);
2N/A return KRB5_CC_NOMEM;
2N/A }
2N/A if (OPENCLOSE(id)) {
2N/A kret = krb5_fcc_open_file(context, id, FCC_OPEN_RDONLY);
2N/A if (kret) {
2N/A free(fcursor);
2N/A k5_cc_mutex_unlock(context, &data->lock);
2N/A return kret;
2N/A }
2N/A }
2N/A
2N/A /* Make sure we start reading right after the primary principal */
2N/A kret = krb5_fcc_skip_header(context, id);
2N/A if (kret) {
2N/A free(fcursor);
2N/A goto done;
2N/A }
2N/A kret = krb5_fcc_skip_principal(context, id);
2N/A if (kret) {
2N/A free(fcursor);
2N/A goto done;
2N/A }
2N/A
2N/A fcursor->pos = fcc_lseek(data, (off_t) 0, SEEK_CUR);
2N/A *cursor = (krb5_cc_cursor) fcursor;
2N/A
2N/Adone:
2N/A MAYBE_CLOSE(context, id, kret);
2N/A k5_cc_mutex_unlock(context, &data->lock);
2N/A return kret;
2N/A}
2N/A
2N/A
2N/A/*
2N/A * Requires:
2N/A * cursor is a krb5_cc_cursor originally obtained from
2N/A * krb5_fcc_start_seq_get.
2N/A *
2N/A * Modifes:
2N/A * cursor, creds
2N/A *
2N/A * Effects:
2N/A * Fills in creds with the "next" credentals structure from the cache
2N/A * id. The actual order the creds are returned in is arbitrary.
2N/A * Space is allocated for the variable length fields in the
2N/A * credentials structure, so the object returned must be passed to
2N/A * krb5_destroy_credential.
2N/A *
2N/A * The cursor is updated for the next call to krb5_fcc_next_cred.
2N/A *
2N/A * Errors:
2N/A * system errors
2N/A */
2N/Astatic krb5_error_code KRB5_CALLCONV
2N/Akrb5_fcc_next_cred(krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor,
2N/A krb5_creds *creds)
2N/A{
2N/A#define TCHECK(ret) if (ret != KRB5_OK) goto lose;
2N/A krb5_error_code kret;
2N/A krb5_fcc_cursor *fcursor;
2N/A krb5_int32 int32;
2N/A krb5_octet octet;
2N/A krb5_fcc_data *d = (krb5_fcc_data *) id->data;
2N/A
2N/A kret = k5_cc_mutex_lock(context, &d->lock);
2N/A if (kret)
2N/A return kret;
2N/A
2N/A memset(creds, 0, sizeof(*creds));
2N/A MAYBE_OPEN(context, id, FCC_OPEN_RDONLY);
2N/A fcursor = (krb5_fcc_cursor *) *cursor;
2N/A
2N/A kret = (fcc_lseek(d, fcursor->pos, SEEK_SET) == (off_t) -1);
2N/A if (kret) {
2N/A kret = krb5_fcc_interpret(context, errno);
2N/A MAYBE_CLOSE(context, id, kret);
2N/A k5_cc_mutex_unlock(context, &d->lock);
2N/A return kret;
2N/A }
2N/A
2N/A kret = krb5_fcc_read_principal(context, id, &creds->client);
2N/A TCHECK(kret);
2N/A kret = krb5_fcc_read_principal(context, id, &creds->server);
2N/A TCHECK(kret);
2N/A kret = krb5_fcc_read_keyblock(context, id, &creds->keyblock);
2N/A TCHECK(kret);
2N/A kret = krb5_fcc_read_times(context, id, &creds->times);
2N/A TCHECK(kret);
2N/A kret = krb5_fcc_read_octet(context, id, &octet);
2N/A TCHECK(kret);
2N/A creds->is_skey = octet;
2N/A kret = krb5_fcc_read_int32(context, id, &int32);
2N/A TCHECK(kret);
2N/A creds->ticket_flags = int32;
2N/A kret = krb5_fcc_read_addrs(context, id, &creds->addresses);
2N/A TCHECK(kret);
2N/A kret = krb5_fcc_read_authdata(context, id, &creds->authdata);
2N/A TCHECK(kret);
2N/A kret = krb5_fcc_read_data(context, id, &creds->ticket);
2N/A TCHECK(kret);
2N/A kret = krb5_fcc_read_data(context, id, &creds->second_ticket);
2N/A TCHECK(kret);
2N/A
2N/A fcursor->pos = fcc_lseek(d, (off_t) 0, SEEK_CUR);
2N/A
2N/Alose:
2N/A MAYBE_CLOSE (context, id, kret);
2N/A k5_cc_mutex_unlock(context, &d->lock);
2N/A if (kret != KRB5_OK)
2N/A krb5_free_cred_contents(context, creds);
2N/A return kret;
2N/A}
2N/A
2N/A/*
2N/A * Requires:
2N/A * cursor is a krb5_cc_cursor originally obtained from
2N/A * krb5_fcc_start_seq_get.
2N/A *
2N/A * Modifies:
2N/A * id, cursor
2N/A *
2N/A * Effects:
2N/A * Finishes sequential processing of the file credentials ccache id,
2N/A * and invalidates the cursor (it must never be used after this call).
2N/A */
2N/A/* ARGSUSED */
2N/Astatic krb5_error_code KRB5_CALLCONV
2N/Akrb5_fcc_end_seq_get(krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor)
2N/A{
2N/A /* We don't do anything with the file cache itself, so
2N/A no need to lock anything. */
2N/A
2N/A /* don't close; it may be left open by the caller,
2N/A and if not, fcc_start_seq_get and/or fcc_next_cred will do the
2N/A MAYBE_CLOSE.
2N/A MAYBE_CLOSE(context, id, kret); */
2N/A free((krb5_fcc_cursor *) *cursor);
2N/A return 0;
2N/A}
2N/A
2N/A
2N/A/*
2N/A * Effects:
2N/A * Creates a new file cred cache whose name is guaranteed to be
2N/A * unique. The name begins with the string TKT_ROOT (from fcc.h).
2N/A * The cache is not opened, but the new filename is reserved.
2N/A *
2N/A * Returns:
2N/A * The filled in krb5_ccache id.
2N/A *
2N/A * Errors:
2N/A * KRB5_CC_NOMEM - there was insufficient memory to allocate the
2N/A * krb5_ccache. id is undefined.
2N/A * system errors (from open)
2N/A */
2N/Astatic krb5_error_code KRB5_CALLCONV
2N/Akrb5_fcc_generate_new (krb5_context context, krb5_ccache *id)
2N/A{
2N/A krb5_ccache lid;
2N/A int ret;
2N/A krb5_error_code kret = 0;
2N/A char scratch[sizeof(TKT_ROOT)+6+1]; /* +6 for the scratch part, +1 for
2N/A NUL */
2N/A krb5_fcc_data *data;
2N/A krb5_int16 fcc_fvno = htons(context->fcc_default_format);
2N/A krb5_int16 fcc_flen = 0;
2N/A int errsave, cnt;
2N/A struct fcc_set *setptr;
2N/A
2N/A /* Set master lock */
2N/A kret = k5_cc_mutex_lock(context, &krb5int_cc_file_mutex);
2N/A if (kret)
2N/A return kret;
2N/A
2N/A (void) snprintf(scratch, sizeof(scratch), "%sXXXXXX", TKT_ROOT);
2N/A ret = mkstemp(scratch);
2N/A if (ret == -1) {
2N/A k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
2N/A return krb5_fcc_interpret(context, errno);
2N/A }
2N/A set_cloexec_fd(ret);
2N/A
2N/A /* Allocate memory */
2N/A data = (krb5_pointer) malloc(sizeof(krb5_fcc_data));
2N/A if (data == NULL) {
2N/A k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
2N/A close(ret);
2N/A unlink(scratch);
2N/A return KRB5_CC_NOMEM;
2N/A }
2N/A
2N/A data->filename = strdup(scratch);
2N/A if (data->filename == NULL) {
2N/A k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
2N/A free(data);
2N/A close(ret);
2N/A unlink(scratch);
2N/A return KRB5_CC_NOMEM;
2N/A }
2N/A
2N/A kret = k5_cc_mutex_init(&data->lock);
2N/A if (kret) {
2N/A k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
2N/A free(data->filename);
2N/A free(data);
2N/A close(ret);
2N/A unlink(scratch);
2N/A return kret;
2N/A }
2N/A kret = k5_cc_mutex_lock(context, &data->lock);
2N/A if (kret) {
2N/A k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
2N/A k5_cc_mutex_destroy(&data->lock);
2N/A free(data->filename);
2N/A free(data);
2N/A close(ret);
2N/A unlink(scratch);
2N/A return kret;
2N/A }
2N/A
2N/A /*
2N/A * The file is initially closed at the end of this call...
2N/A */
2N/A data->flags = 0;
2N/A data->file = -1;
2N/A data->valid_bytes = 0;
2N/A /* data->version,mode filled in for real later */
2N/A data->version = data->mode = 0;
2N/A
2N/A
2N/A /* Ignore user's umask, set mode = 0600 */
2N/A#ifndef HAVE_FCHMOD
2N/A#ifdef HAVE_CHMOD
2N/A chmod(data->filename, S_IRUSR | S_IWUSR);
2N/A#endif
2N/A#else
2N/A fchmod(ret, S_IRUSR | S_IWUSR);
2N/A#endif
2N/A if ((cnt = write(ret, (char *)&fcc_fvno, sizeof(fcc_fvno)))
2N/A != sizeof(fcc_fvno)) {
2N/A errsave = errno;
2N/A (void) close(ret);
2N/A (void) unlink(data->filename);
2N/A kret = (cnt == -1) ? krb5_fcc_interpret(context, errsave) : KRB5_CC_IO;
2N/A goto err_out;
2N/A }
2N/A /* For version 4 we save a length for the rest of the header */
2N/A if (context->fcc_default_format == KRB5_FCC_FVNO_4) {
2N/A if ((cnt = write(ret, (char *)&fcc_flen, sizeof(fcc_flen)))
2N/A != sizeof(fcc_flen)) {
2N/A errsave = errno;
2N/A (void) close(ret);
2N/A (void) unlink(data->filename);
2N/A kret = (cnt == -1) ? krb5_fcc_interpret(context, errsave) : KRB5_CC_IO;
2N/A goto err_out;
2N/A }
2N/A }
2N/A if (close(ret) == -1) {
2N/A errsave = errno;
2N/A (void) unlink(data->filename);
2N/A kret = krb5_fcc_interpret(context, errsave);
2N/A goto err_out;
2N/A }
2N/A
2N/A
2N/A setptr = malloc(sizeof(struct fcc_set));
2N/A if (setptr == NULL) {
2N/A k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
2N/A k5_cc_mutex_unlock(context, &data->lock);
2N/A k5_cc_mutex_destroy(&data->lock);
2N/A free(data->filename);
2N/A free(data);
2N/A (void) close(ret);
2N/A (void) unlink(scratch);
2N/A return KRB5_CC_NOMEM;
2N/A }
2N/A setptr->refcount = 1;
2N/A setptr->data = data;
2N/A setptr->next = fccs;
2N/A fccs = setptr;
2N/A k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
2N/A
2N/A k5_cc_mutex_assert_locked(context, &data->lock);
2N/A k5_cc_mutex_unlock(context, &data->lock);
2N/A lid = (krb5_ccache) malloc(sizeof(struct _krb5_ccache));
2N/A if (lid == NULL) {
2N/A dereference(context, data);
2N/A return KRB5_CC_NOMEM;
2N/A }
2N/A
2N/A lid->ops = &krb5_fcc_ops;
2N/A lid->data = data;
2N/A lid->magic = KV5M_CCACHE;
2N/A
2N/A /* default to open/close on every trn - otherwise destroy
2N/A will get as to state confused */
2N/A ((krb5_fcc_data *) lid->data)->flags = KRB5_TC_OPENCLOSE;
2N/A
2N/A *id = lid;
2N/A
2N/A
2N/A krb5_change_cache ();
2N/A return KRB5_OK;
2N/A
2N/Aerr_out:
2N/A k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
2N/A k5_cc_mutex_unlock(context, &data->lock);
2N/A k5_cc_mutex_destroy(&data->lock);
2N/A free(data->filename);
2N/A free(data);
2N/A return kret;
2N/A}
2N/A
2N/A/*
2N/A * Requires:
2N/A * id is a file credential cache
2N/A *
2N/A * Returns:
2N/A * The name of the file cred cache id.
2N/A */
2N/Astatic const char * KRB5_CALLCONV
2N/Akrb5_fcc_get_name (krb5_context context, krb5_ccache id)
2N/A{
2N/A return (char *) ((krb5_fcc_data *) id->data)->filename;
2N/A}
2N/A
2N/A/*
2N/A * Modifies:
2N/A * id, princ
2N/A *
2N/A * Effects:
2N/A * Retrieves the primary principal from id, as set with
2N/A * krb5_fcc_initialize. The principal is returned is allocated
2N/A * storage that must be freed by the caller via krb5_free_principal.
2N/A *
2N/A * Errors:
2N/A * system errors
2N/A * KRB5_CC_NOMEM
2N/A */
2N/Astatic krb5_error_code KRB5_CALLCONV
2N/Akrb5_fcc_get_principal(krb5_context context, krb5_ccache id, krb5_principal *princ)
2N/A{
2N/A krb5_error_code kret = KRB5_OK;
2N/A
2N/A kret = k5_cc_mutex_lock(context, &((krb5_fcc_data *) id->data)->lock);
2N/A if (kret)
2N/A return kret;
2N/A
2N/A MAYBE_OPEN(context, id, FCC_OPEN_RDONLY);
2N/A
2N/A /* make sure we're beyond the header */
2N/A kret = krb5_fcc_skip_header(context, id);
2N/A if (kret) goto done;
2N/A kret = krb5_fcc_read_principal(context, id, princ);
2N/A
2N/Adone:
2N/A MAYBE_CLOSE(context, id, kret);
2N/A k5_cc_mutex_unlock(context, &((krb5_fcc_data *) id->data)->lock);
2N/A return kret;
2N/A}
2N/A
2N/A
2N/Astatic krb5_error_code KRB5_CALLCONV
2N/Akrb5_fcc_retrieve(krb5_context context, krb5_ccache id, krb5_flags whichfields, krb5_creds *mcreds, krb5_creds *creds)
2N/A{
2N/A return krb5_cc_retrieve_cred_default (context, id, whichfields,
2N/A mcreds, creds);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * Modifies:
2N/A * the file cache
2N/A *
2N/A * Effects:
2N/A * stores creds in the file cred cache
2N/A *
2N/A * Errors:
2N/A * system errors
2N/A * storage failure errors
2N/A */
2N/Astatic krb5_error_code KRB5_CALLCONV
2N/Akrb5_fcc_store(krb5_context context, krb5_ccache id, krb5_creds *creds)
2N/A{
2N/A#define TCHECK(ret) if (ret != KRB5_OK) goto lose;
2N/A krb5_error_code ret;
2N/A
2N/A ret = k5_cc_mutex_lock(context, &((krb5_fcc_data *) id->data)->lock);
2N/A if (ret)
2N/A return ret;
2N/A
2N/A /* Make sure we are writing to the end of the file */
2N/A MAYBE_OPEN(context, id, FCC_OPEN_RDWR);
2N/A
2N/A /* Make sure we are writing to the end of the file */
2N/A ret = fcc_lseek((krb5_fcc_data *) id->data, (off_t) 0, SEEK_END);
2N/A if (ret < 0) {
2N/A MAYBE_CLOSE_IGNORE(context, id);
2N/A k5_cc_mutex_unlock(context, &((krb5_fcc_data *) id->data)->lock);
2N/A return krb5_fcc_interpret(context, errno);
2N/A }
2N/A
2N/A ret = krb5_fcc_store_principal(context, id, creds->client);
2N/A TCHECK(ret);
2N/A ret = krb5_fcc_store_principal(context, id, creds->server);
2N/A TCHECK(ret);
2N/A ret = krb5_fcc_store_keyblock(context, id, &creds->keyblock);
2N/A TCHECK(ret);
2N/A ret = krb5_fcc_store_times(context, id, &creds->times);
2N/A TCHECK(ret);
2N/A ret = krb5_fcc_store_octet(context, id, (krb5_int32) creds->is_skey);
2N/A TCHECK(ret);
2N/A ret = krb5_fcc_store_int32(context, id, creds->ticket_flags);
2N/A TCHECK(ret);
2N/A ret = krb5_fcc_store_addrs(context, id, creds->addresses);
2N/A TCHECK(ret);
2N/A ret = krb5_fcc_store_authdata(context, id, creds->authdata);
2N/A TCHECK(ret);
2N/A ret = krb5_fcc_store_data(context, id, &creds->ticket);
2N/A TCHECK(ret);
2N/A ret = krb5_fcc_store_data(context, id, &creds->second_ticket);
2N/A TCHECK(ret);
2N/A
2N/Alose:
2N/A MAYBE_CLOSE(context, id, ret);
2N/A k5_cc_mutex_unlock(context, &((krb5_fcc_data *) id->data)->lock);
2N/A krb5_change_cache ();
2N/A return ret;
2N/A#undef TCHECK
2N/A}
2N/A
2N/A/*
2N/A * Non-functional stub implementation for krb5_fcc_remove
2N/A *
2N/A * Errors:
2N/A * KRB5_CC_NOSUPP - not implemented
2N/A */
2N/Astatic krb5_error_code KRB5_CALLCONV
2N/Akrb5_fcc_remove_cred(krb5_context context, krb5_ccache cache, krb5_flags flags,
2N/A krb5_creds *creds)
2N/A{
2N/A return KRB5_CC_NOSUPP;
2N/A}
2N/A
2N/A/*
2N/A * Requires:
2N/A * id is a cred cache returned by krb5_fcc_resolve or
2N/A * krb5_fcc_generate_new, but has not been opened by krb5_fcc_initialize.
2N/A *
2N/A * Modifies:
2N/A * id
2N/A *
2N/A * Effects:
2N/A * Sets the operational flags of id to flags.
2N/A */
2N/Astatic krb5_error_code KRB5_CALLCONV
2N/Akrb5_fcc_set_flags(krb5_context context, krb5_ccache id, krb5_flags flags)
2N/A{
2N/A krb5_error_code ret = KRB5_OK;
2N/A
2N/A ret = k5_cc_mutex_lock(context, &((krb5_fcc_data *) id->data)->lock);
2N/A if (ret)
2N/A return ret;
2N/A
2N/A /* XXX This should check for illegal combinations, if any.. */
2N/A if (flags & KRB5_TC_OPENCLOSE) {
2N/A /* asking to turn on OPENCLOSE mode */
2N/A if (!OPENCLOSE(id)
2N/A /* XXX Is this test necessary? */
2N/A && ((krb5_fcc_data *) id->data)->file != NO_FILE)
2N/A (void) krb5_fcc_close_file (context, ((krb5_fcc_data *) id->data));
2N/A } else {
2N/A /* asking to turn off OPENCLOSE mode, meaning it must be
2N/A left open. We open if it's not yet open */
2N/A MAYBE_OPEN(context, id, FCC_OPEN_RDONLY);
2N/A }
2N/A
2N/A ((krb5_fcc_data *) id->data)->flags = flags;
2N/A k5_cc_mutex_unlock(context, &((krb5_fcc_data *) id->data)->lock);
2N/A return ret;
2N/A}
2N/A
2N/A/*
2N/A * Requires:
2N/A * id is a cred cache returned by krb5_fcc_resolve or
2N/A * krb5_fcc_generate_new, but has not been opened by krb5_fcc_initialize.
2N/A *
2N/A * Modifies:
2N/A * id (mutex only; temporary)
2N/A *
2N/A * Effects:
2N/A * Returns the operational flags of id.
2N/A */
2N/Astatic krb5_error_code KRB5_CALLCONV
2N/Akrb5_fcc_get_flags(krb5_context context, krb5_ccache id, krb5_flags *flags)
2N/A{
2N/A krb5_error_code ret = KRB5_OK;
2N/A
2N/A ret = k5_cc_mutex_lock(context, &((krb5_fcc_data *) id->data)->lock);
2N/A if (ret)
2N/A return ret;
2N/A *flags = ((krb5_fcc_data *) id->data)->flags;
2N/A k5_cc_mutex_unlock(context, &((krb5_fcc_data *) id->data)->lock);
2N/A return ret;
2N/A}
2N/A
2N/Astatic krb5_error_code KRB5_CALLCONV
2N/Akrb5_fcc_ptcursor_new(krb5_context context, krb5_cc_ptcursor *cursor)
2N/A{
2N/A krb5_error_code ret = 0;
2N/A krb5_cc_ptcursor n = NULL;
2N/A struct krb5_fcc_ptcursor_data *cdata = NULL;
2N/A
2N/A *cursor = NULL;
2N/A
2N/A n = malloc(sizeof(*n));
2N/A if (n == NULL)
2N/A return ENOMEM;
2N/A n->ops = &krb5_fcc_ops;
2N/A cdata = malloc(sizeof(struct krb5_fcc_ptcursor_data));
2N/A if (cdata == NULL) {
2N/A ret = ENOMEM;
2N/A goto errout;
2N/A }
2N/A n->data = cdata;
2N/A ret = k5_cc_mutex_lock(context, &krb5int_cc_file_mutex);
2N/A if (ret)
2N/A goto errout;
2N/A cdata->cur = fccs;
2N/A ret = k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
2N/A if (ret)
2N/A goto errout;
2N/A
2N/Aerrout:
2N/A if (ret) {
2N/A krb5_fcc_ptcursor_free(context, &n);
2N/A }
2N/A *cursor = n;
2N/A return ret;
2N/A}
2N/A
2N/Astatic krb5_error_code KRB5_CALLCONV
2N/Akrb5_fcc_ptcursor_next(krb5_context context,
2N/A krb5_cc_ptcursor cursor,
2N/A krb5_ccache *ccache)
2N/A{
2N/A krb5_error_code ret = 0;
2N/A struct krb5_fcc_ptcursor_data *cdata = NULL;
2N/A krb5_ccache n;
2N/A
2N/A *ccache = NULL;
2N/A n = malloc(sizeof(*n));
2N/A if (n == NULL)
2N/A return ENOMEM;
2N/A
2N/A cdata = cursor->data;
2N/A
2N/A ret = k5_cc_mutex_lock(context, &krb5int_cc_file_mutex);
2N/A if (ret)
2N/A goto errout;
2N/A
2N/A if (cdata->cur == NULL) {
2N/A k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
2N/A free(n);
2N/A n = NULL;
2N/A goto errout;
2N/A }
2N/A
2N/A n->ops = &krb5_fcc_ops;
2N/A n->data = cdata->cur->data;
2N/A cdata->cur->refcount++;
2N/A
2N/A cdata->cur = cdata->cur->next;
2N/A
2N/A ret = k5_cc_mutex_unlock(context, &krb5int_cc_file_mutex);
2N/A if (ret)
2N/A goto errout;
2N/Aerrout:
2N/A if (ret && n != NULL) {
2N/A free(n);
2N/A n = NULL;
2N/A }
2N/A *ccache = n;
2N/A return ret;
2N/A}
2N/A
2N/Astatic krb5_error_code KRB5_CALLCONV
2N/Akrb5_fcc_ptcursor_free(krb5_context context,
2N/A krb5_cc_ptcursor *cursor)
2N/A{
2N/A if (*cursor == NULL)
2N/A return 0;
2N/A if ((*cursor)->data != NULL)
2N/A free((*cursor)->data);
2N/A free(*cursor);
2N/A *cursor = NULL;
2N/A return 0;
2N/A}
2N/A
2N/A/*
2N/A * Modifies:
2N/A * change_time
2N/A *
2N/A * Effects:
2N/A * Returns the timestamp of id's file modification date.
2N/A * If an error occurs, change_time is set to 0.
2N/A */
2N/Astatic krb5_error_code KRB5_CALLCONV
2N/Akrb5_fcc_last_change_time(krb5_context context, krb5_ccache id,
2N/A krb5_timestamp *change_time)
2N/A{
2N/A krb5_error_code kret = KRB5_OK;
2N/A krb5_fcc_data *data = (krb5_fcc_data *) id->data;
2N/A
2N/A kret = krb5_fcc_data_last_change_time(context, data, change_time);
2N/A
2N/A return kret;
2N/A}
2N/A
2N/Astatic krb5_error_code KRB5_CALLCONV
2N/Akrb5_fcc_lock(krb5_context context, krb5_ccache id)
2N/A{
2N/A krb5_error_code ret = 0;
2N/A krb5_fcc_data *data = (krb5_fcc_data *) id->data;
2N/A ret = k5_cc_mutex_lock(context, &data->lock);
2N/A return ret;
2N/A}
2N/A
2N/Astatic krb5_error_code KRB5_CALLCONV
2N/Akrb5_fcc_unlock(krb5_context context, krb5_ccache id)
2N/A{
2N/A krb5_error_code ret = 0;
2N/A krb5_fcc_data *data = (krb5_fcc_data *) id->data;
2N/A ret = k5_cc_mutex_unlock(context, &data->lock);
2N/A return ret;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Akrb5_fcc_data_last_change_time(krb5_context context, krb5_fcc_data *data,
2N/A krb5_timestamp *change_time)
2N/A{
2N/A krb5_error_code kret = KRB5_OK;
2N/A register int ret;
2N/A
2N/A struct stat buf;
2N/A
2N/A *change_time = 0;
2N/A
2N/A kret = k5_cc_mutex_lock(context, &data->lock);
2N/A if (kret) {
2N/A return kret;
2N/A }
2N/A
2N/A ret = stat(data->filename, &buf);
2N/A if (ret == -1) {
2N/A kret = krb5_fcc_interpret(context, errno);
2N/A } else {
2N/A *change_time = (krb5_timestamp) buf.st_mtime;
2N/A }
2N/A
2N/A k5_cc_mutex_unlock(context, &data->lock);
2N/A
2N/A return kret;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Akrb5_fcc_interpret(krb5_context context, int errnum)
2N/A{
2N/A register krb5_error_code retval;
2N/A switch (errnum) {
2N/A case ENOENT:
2N/A retval = KRB5_FCC_NOFILE;
2N/A break;
2N/A case EPERM:
2N/A case EACCES:
2N/A#ifdef EISDIR
2N/A case EISDIR: /* Mac doesn't have EISDIR */
2N/A#endif
2N/A case ENOTDIR:
2N/A#ifdef ELOOP
2N/A case ELOOP: /* Bad symlink is like no file. */
2N/A#endif
2N/A#ifdef ETXTBSY
2N/A case ETXTBSY:
2N/A#endif
2N/A case EBUSY:
2N/A case EROFS:
2N/A retval = KRB5_FCC_PERM;
2N/A break;
2N/A case EINVAL:
2N/A case EEXIST: /* XXX */
2N/A case EFAULT:
2N/A case EBADF:
2N/A#ifdef ENAMETOOLONG
2N/A case ENAMETOOLONG:
2N/A#endif
2N/A#ifdef EWOULDBLOCK
2N/A case EWOULDBLOCK:
2N/A#endif
2N/A retval = KRB5_FCC_INTERNAL;
2N/A break;
2N/A#ifdef EDQUOT
2N/A case EDQUOT:
2N/A#endif
2N/A case ENOSPC:
2N/A case EIO:
2N/A case ENFILE:
2N/A case EMFILE:
2N/A case ENXIO:
2N/A default:
2N/A retval = KRB5_CC_IO; /* XXX */
2N/A krb5_set_error_message(context, retval,
2N/A dgettext(TEXT_DOMAIN,
2N/A "Credentials cache I/O operation failed (%s)"),
2N/A strerror(errnum));
2N/A }
2N/A return retval;
2N/A}
2N/A
2N/Aconst krb5_cc_ops krb5_fcc_ops = {
2N/A 0,
2N/A "FILE",
2N/A krb5_fcc_get_name,
2N/A krb5_fcc_resolve,
2N/A krb5_fcc_generate_new,
2N/A krb5_fcc_initialize,
2N/A krb5_fcc_destroy,
2N/A krb5_fcc_close,
2N/A krb5_fcc_store,
2N/A krb5_fcc_retrieve,
2N/A krb5_fcc_get_principal,
2N/A krb5_fcc_start_seq_get,
2N/A krb5_fcc_next_cred,
2N/A krb5_fcc_end_seq_get,
2N/A krb5_fcc_remove_cred,
2N/A krb5_fcc_set_flags,
2N/A krb5_fcc_get_flags,
2N/A krb5_fcc_ptcursor_new,
2N/A krb5_fcc_ptcursor_next,
2N/A krb5_fcc_ptcursor_free,
2N/A NULL, /* move */
2N/A krb5_fcc_last_change_time,
2N/A NULL, /* wasdefault */
2N/A krb5_fcc_lock,
2N/A krb5_fcc_unlock,
2N/A};
2N/A
2N/A#if defined(_WIN32)
2N/A/*
2N/A * krb5_change_cache should be called after the cache changes.
2N/A * A notification message is is posted out to all top level
2N/A * windows so that they may recheck the cache based on the
2N/A * changes made. We register a unique message type with which
2N/A * we'll communicate to all other processes.
2N/A */
2N/A
2N/Akrb5_error_code
2N/Akrb5_change_cache (void) {
2N/A
2N/A PostMessage(HWND_BROADCAST, krb5_get_notification_message(), 0, 0);
2N/A
2N/A return 0;
2N/A}
2N/A
2N/Aunsigned int KRB5_CALLCONV
2N/Akrb5_get_notification_message (void) {
2N/A static unsigned int message = 0;
2N/A
2N/A if (message == 0)
2N/A message = RegisterWindowMessage(WM_KERBEROS5_CHANGED);
2N/A
2N/A return message;
2N/A}
2N/A#else /* _WIN32 */
2N/A
2N/Akrb5_error_code
2N/Akrb5_change_cache (void)
2N/A{
2N/A return 0;
2N/A}
2N/Aunsigned int
2N/Akrb5_get_notification_message (void)
2N/A{
2N/A return 0;
2N/A}
2N/A
2N/A#endif /* _WIN32 */
2N/A
2N/Aconst krb5_cc_ops krb5_cc_file_ops = {
2N/A 0,
2N/A "FILE",
2N/A krb5_fcc_get_name,
2N/A krb5_fcc_resolve,
2N/A krb5_fcc_generate_new,
2N/A krb5_fcc_initialize,
2N/A krb5_fcc_destroy,
2N/A krb5_fcc_close,
2N/A krb5_fcc_store,
2N/A krb5_fcc_retrieve,
2N/A krb5_fcc_get_principal,
2N/A krb5_fcc_start_seq_get,
2N/A krb5_fcc_next_cred,
2N/A krb5_fcc_end_seq_get,
2N/A krb5_fcc_remove_cred,
2N/A krb5_fcc_set_flags,
2N/A krb5_fcc_get_flags,
2N/A krb5_fcc_ptcursor_new,
2N/A krb5_fcc_ptcursor_next,
2N/A krb5_fcc_ptcursor_free,
2N/A NULL, /* move */
2N/A krb5_fcc_last_change_time,
2N/A NULL, /* wasdefault */
2N/A krb5_fcc_lock,
2N/A krb5_fcc_unlock,
2N/A};