Beispiel #1
0
static krb5_error_code
ctx_lock(krb5_context context, krb5_db2_context *dbc, int lockmode)
{
    krb5_error_code retval;
    int kmode, tries;

    if (lockmode == KRB5_DB_LOCKMODE_PERMANENT ||
        lockmode == KRB5_DB_LOCKMODE_EXCLUSIVE)
        kmode = KRB5_LOCKMODE_EXCLUSIVE;
    else if (lockmode == KRB5_DB_LOCKMODE_SHARED)
        kmode = KRB5_LOCKMODE_SHARED;
    else
        return EINVAL;

    if (dbc->db_locks_held == 0 || dbc->db_lock_mode < kmode) {
        /* Acquire or upgrade the lock. */
        for (tries = 0; tries < MAX_LOCK_TRIES; tries++) {
            retval = krb5_lock_file(context, dbc->db_lf_file,
                                    kmode | KRB5_LOCKMODE_DONTBLOCK);
            if (retval == 0)
                break;
            if (retval == EBADF && kmode == KRB5_LOCKMODE_EXCLUSIVE)
                /* Tried to lock something we don't have write access to. */
                return KRB5_KDB_CANTLOCK_DB;
            sleep(1);
        }
        if (retval == EACCES)
            return KRB5_KDB_CANTLOCK_DB;
        else if (retval == EAGAIN || retval == EWOULDBLOCK)
            return OSA_ADB_CANTLOCK_DB;
        else if (retval)
            return retval;

        /* Open the DB (or re-open it for read/write). */
        if (dbc->db != NULL)
            dbc->db->close(dbc->db);
        dbc->db = open_db(dbc,
                          kmode == KRB5_LOCKMODE_SHARED ? O_RDONLY : O_RDWR,
                          0600);
        if (dbc->db == NULL) {
            retval = errno;
            dbc->db_locks_held = 0;
            dbc->db_lock_mode = 0;
            (void) osa_adb_release_lock(dbc->policy_db);
            (void) krb5_lock_file(context, dbc->db_lf_file,
                                  KRB5_LOCKMODE_UNLOCK);
            return retval;
        }

        dbc->db_lock_mode = kmode;
    }
    dbc->db_locks_held++;

    /* Acquire or upgrade the policy lock. */
    retval = osa_adb_get_lock(dbc->policy_db, lockmode);
    if (retval)
        (void) ctx_unlock(context, dbc);
    return retval;
}
Beispiel #2
0
static krb5_error_code
ctx_lock(krb5_context context, krb5_db2_context *dbc, int lockmode)
{
    krb5_error_code retval;
    int kmode;

    if (lockmode == KRB5_DB_LOCKMODE_PERMANENT ||
        lockmode == KRB5_DB_LOCKMODE_EXCLUSIVE)
        kmode = KRB5_LOCKMODE_EXCLUSIVE;
    else if (lockmode == KRB5_DB_LOCKMODE_SHARED)
        kmode = KRB5_LOCKMODE_SHARED;
    else
        return EINVAL;

    if (dbc->db_locks_held == 0 || dbc->db_lock_mode < kmode) {
        /* Acquire or upgrade the lock. */
        retval = krb5_lock_file(context, dbc->db_lf_file, kmode);
        /* Check if we tried to lock something not open for write. */
        if (retval == EBADF && kmode == KRB5_LOCKMODE_EXCLUSIVE)
            return KRB5_KDB_CANTLOCK_DB;
        else if (retval == EACCES || retval == EAGAIN || retval == EWOULDBLOCK)
            return KRB5_KDB_CANTLOCK_DB;
        else if (retval)
            return retval;

        /* Open the DB (or re-open it for read/write). */
        if (dbc->db != NULL)
            dbc->db->close(dbc->db);
        dbc->db = open_db(dbc,
                          kmode == KRB5_LOCKMODE_SHARED ? O_RDONLY : O_RDWR,
                          0600);
        if (dbc->db == NULL) {
            retval = errno;
            dbc->db_locks_held = 0;
            dbc->db_lock_mode = 0;
            (void) osa_adb_release_lock(dbc->policy_db);
            (void) krb5_lock_file(context, dbc->db_lf_file,
                                  KRB5_LOCKMODE_UNLOCK);
            return retval;
        }

        dbc->db_lock_mode = kmode;
    }
    dbc->db_locks_held++;

    /* Acquire or upgrade the policy lock. */
    retval = osa_adb_get_lock(dbc->policy_db, lockmode);
    if (retval) {
        (void) ctx_unlock(context, dbc);
        if (retval == OSA_ADB_NOEXCL_PERM || retval == OSA_ADB_CANTLOCK_DB ||
            retval == OSA_ADB_NOLOCKFILE)
            retval = KRB5_KDB_CANTLOCK_DB;
    }
    return retval;
}
Beispiel #3
0
krb5_error_code
krb5_db2_unlock(krb5_context context)
{
    krb5_db2_context *db_ctx;
    DB     *db;
    krb5_error_code retval;

    if (!k5db2_inited(context))
        return KRB5_KDB_DBNOTINITED;

    db_ctx = context->dal_handle->db_context;

    if ((retval = osa_adb_release_lock(db_ctx->policy_db))) {
        return retval;
    }

    if (!db_ctx->db_locks_held) /* lock already unlocked */
        return KRB5_KDB_NOTLOCKED;
    db = db_ctx->db;
    if (--(db_ctx->db_locks_held) == 0) {
        (*db->close) (db);
        db_ctx->db = NULL;

        retval = krb5_lock_file(context, db_ctx->db_lf_file,
                                KRB5_LOCKMODE_UNLOCK);
        db_ctx->db_lock_mode = 0;
        return (retval);
    }
    return 0;
}
Beispiel #4
0
krb5_error_code
osa_adb_release_lock(osa_adb_db_t db)
{
    int ret, fd;

    if (!db->lock->lockcnt)            /* lock already unlocked */
        return OSA_ADB_NOTLOCKED;

    if (--db->lock->lockcnt == 0) {
        if (db->lock->lockmode == KRB5_DB_LOCKMODE_PERMANENT) {
            /* now we need to create the file since it does not exist */
            fd = THREEPARAMOPEN(db->lock->filename,O_RDWR | O_CREAT | O_EXCL,
                                0600);
            if (fd < 0)
                return OSA_ADB_NOLOCKFILE;
            set_cloexec_fd(fd);
            if ((db->lock->lockfile = fdopen(fd, "w+")) == NULL)
                return OSA_ADB_NOLOCKFILE;
        } else if ((ret = krb5_lock_file(db->lock->context,
                                         fileno(db->lock->lockfile),
                                         KRB5_LOCKMODE_UNLOCK)))
            return ret;

        db->lock->lockmode = 0;
    }
    return OSA_ADB_OK;
}
Beispiel #5
0
static krb5_error_code
ctx_unlock(krb5_context context, krb5_db2_context *dbc)
{
    krb5_error_code retval, retval2;
    DB *db;

    retval = osa_adb_release_lock(dbc->policy_db);

    if (!dbc->db_locks_held) /* lock already unlocked */
        return KRB5_KDB_NOTLOCKED;

    db = dbc->db;
    if (--(dbc->db_locks_held) == 0) {
        db->close(db);
        dbc->db = NULL;
        dbc->db_lock_mode = 0;

        retval2 = krb5_lock_file(context, dbc->db_lf_file,
                                KRB5_LOCKMODE_UNLOCK);
        if (retval2)
            return retval2;
    }

    /* We may be unlocking because osa_adb_get_lock() failed. */
    if (retval == OSA_ADB_NOTLOCKED)
        return 0;
    return retval;
}
Beispiel #6
0
/*
 * If any database operations will be invoked while the ulog lock is held, the
 * caller must explicitly lock the database before locking the ulog, or
 * deadlock may result.
 */
static krb5_error_code
lock_ulog(krb5_context context, int mode)
{
    kdb_log_context *log_ctx = NULL;
    kdb_hlog_t *ulog = NULL;

    INIT_ULOG(context);
    return krb5_lock_file(context, log_ctx->ulogfd, mode);
}
Beispiel #7
0
static void
close_database(krb5_context context, int fd)
{
    int err;

    err = krb5_lock_file(context, fd, KRB5_LOCKMODE_UNLOCK);
    if (err)
        com_err(progname, err, _("while unlocking database '%s'"), dbpathname);
    free(dbpathname);
    close(fd);
}
Beispiel #8
0
krb5_error_code
ulog_lock(krb5_context ctx, int mode)
{
    kdb_log_context *log_ctx = NULL;
    kdb_hlog_t *ulog = NULL;

    if (ctx == NULL)
        return KRB5_LOG_ERROR;
    if (ctx->kdblog_context == NULL || ctx->kdblog_context->iproprole == IPROP_NULL)
        return 0;
    INIT_ULOG(ctx);
    return krb5_lock_file(ctx, log_ctx->ulogfd, mode);
}
Beispiel #9
0
/*
 * Open the Kerberos database dump file.  Takes care of locking it
 * and making sure that the .ok file is more recent that the database
 * dump file itself.
 *
 * Returns the file descriptor of the database dump file.  Also fills
 * in the size of the database file.
 */
static int
open_database(krb5_context context, char *data_fn, int *size)
{
    struct stat stbuf, stbuf_ok;
    char *data_ok_fn;
    int fd, err;

    dbpathname = strdup(data_fn);
    if (dbpathname == NULL) {
        com_err(progname, ENOMEM, _("allocating database file name '%s'"),
                data_fn);
        exit(1);
    }
    fd = open(dbpathname, O_RDONLY);
    if (fd < 0) {
        com_err(progname, errno, _("while trying to open %s"), dbpathname);
        exit(1);
    }

    err = krb5_lock_file(context, fd,
                         KRB5_LOCKMODE_SHARED | KRB5_LOCKMODE_DONTBLOCK);
    if (err == EAGAIN || err == EWOULDBLOCK || errno == EACCES) {
        com_err(progname, 0, _("database locked"));
        exit(1);
    } else if (err) {
        com_err(progname, err, _("while trying to lock '%s'"), dbpathname);
        exit(1);
    }
    if (fstat(fd, &stbuf)) {
        com_err(progname, errno, _("while trying to stat %s"), data_fn);
        exit(1);
    }
    if (asprintf(&data_ok_fn, "%s.dump_ok", data_fn) < 0) {
        com_err(progname, ENOMEM, _("while trying to malloc data_ok_fn"));
        exit(1);
    }
    if (stat(data_ok_fn, &stbuf_ok)) {
        com_err(progname, errno, _("while trying to stat %s"), data_ok_fn);
        free(data_ok_fn);
        exit(1);
    }
    if (stbuf.st_mtime > stbuf_ok.st_mtime) {
        com_err(progname, 0, _("'%s' more recent than '%s'."), data_fn,
                data_ok_fn);
        exit(1);
    }
    free(data_ok_fn);
    *size = stbuf.st_size;
    return fd;
}
Beispiel #10
0
static krb5_error_code
ctx_unlock(krb5_context context, krb5_db2_context *dbc)
{
    krb5_error_code retval;
    DB *db;

    retval = osa_adb_release_lock(dbc->policy_db);
    if (retval)
        return retval;

    if (!dbc->db_locks_held) /* lock already unlocked */
        return KRB5_KDB_NOTLOCKED;

    db = dbc->db;
    if (--(dbc->db_locks_held) == 0) {
        db->close(db);
        dbc->db = NULL;
        dbc->db_lock_mode = 0;

        retval = krb5_lock_file(context, dbc->db_lf_file,
                                KRB5_LOCKMODE_UNLOCK);
    }
    return retval;
}
Beispiel #11
0
krb5_error_code
krb5_db2_lock(krb5_context context, int in_mode)
{
    krb5_db2_context *db_ctx;
    int     krb5_lock_mode;
    DB     *db;
    krb5_error_code retval;
    time_t  mod_time;
    int     mode, gotlock, tries;

    switch (in_mode) {
    case KRB5_DB_LOCKMODE_PERMANENT:
        mode = KRB5_DB_LOCKMODE_EXCLUSIVE;
        break;
    case KRB5_DB_LOCKMODE_EXCLUSIVE:
        mode = KRB5_LOCKMODE_EXCLUSIVE;
        break;

    case KRB5_DB_LOCKMODE_SHARED:
        mode = KRB5_LOCKMODE_SHARED;
        break;
    default:
        return EINVAL;
    }

    if (!k5db2_inited(context))
        return KRB5_KDB_DBNOTINITED;

    db_ctx = context->dal_handle->db_context;
    if (db_ctx->db_locks_held && (db_ctx->db_lock_mode >= mode)) {
        /* No need to upgrade lock, just return */
        db_ctx->db_locks_held++;
        goto policy_lock;
    }

    if ((mode != KRB5_LOCKMODE_SHARED) && (mode != KRB5_LOCKMODE_EXCLUSIVE))
        return KRB5_KDB_BADLOCKMODE;

    krb5_lock_mode = mode | KRB5_LOCKMODE_DONTBLOCK;
    for (gotlock = tries = 0; tries < MAX_LOCK_TRIES; tries++) {
        retval = krb5_lock_file(context, db_ctx->db_lf_file, krb5_lock_mode);
        if (retval == 0) {
            gotlock++;
            break;
        } else if (retval == EBADF && mode == KRB5_DB_LOCKMODE_EXCLUSIVE)
            /* tried to exclusive-lock something we don't have */
            /* write access to */
            return KRB5_KDB_CANTLOCK_DB;
        sleep(1);
    }
    if (retval == EACCES)
        return KRB5_KDB_CANTLOCK_DB;
    else if (retval == EAGAIN || retval == EWOULDBLOCK)
        return OSA_ADB_CANTLOCK_DB;
    else if (retval != 0)
        return retval;

    if ((retval = krb5_db2_get_age(context, NULL, &mod_time)))
        goto lock_error;

    db = k5db2_dbopen(db_ctx, db_ctx->db_name,
                      mode == KRB5_LOCKMODE_SHARED ? O_RDONLY : O_RDWR, 0600, db_ctx->tempdb);
    if (db) {
        db_ctx->db_lf_time = mod_time;
        db_ctx->db = db;
    } else {
        retval = errno;
        db_ctx->db = NULL;
        goto lock_error;
    }

    db_ctx->db_lock_mode = mode;
    db_ctx->db_locks_held++;

policy_lock:
    if ((retval = osa_adb_get_lock(db_ctx->policy_db, in_mode))) {
        krb5_db2_unlock(context);
    }
    return retval;

lock_error:;
    db_ctx->db_lock_mode = 0;
    db_ctx->db_locks_held = 0;
    krb5_db2_unlock(context);
    return retval;
}
Beispiel #12
0
krb5_error_code
osa_adb_get_lock(osa_adb_db_t db, int mode)
{
    int perm, krb5_mode, ret = 0;

    if (db->lock->lockmode >= mode) {
        /* No need to upgrade lock, just incr refcnt and return */
        db->lock->lockcnt++;
        return(OSA_ADB_OK);
    }

    perm = 0;
    switch (mode) {
    case KRB5_DB_LOCKMODE_PERMANENT:
        perm = 1;
    case KRB5_DB_LOCKMODE_EXCLUSIVE:
        krb5_mode = KRB5_LOCKMODE_EXCLUSIVE;
        break;
    case KRB5_DB_LOCKMODE_SHARED:
        krb5_mode = KRB5_LOCKMODE_SHARED;
        break;
    default:
        return(EINVAL);
    }

    ret = krb5_lock_file(db->lock->context, fileno(db->lock->lockfile),
                         krb5_mode);
    if (ret == EBADF && mode == KRB5_DB_LOCKMODE_EXCLUSIVE)
        return OSA_ADB_NOEXCL_PERM;
    else if (ret == EACCES || ret == EAGAIN || ret == EWOULDBLOCK)
        return OSA_ADB_CANTLOCK_DB;
    else if (ret != 0)
        return ret;

    /*
     * If the file no longer exists, someone acquired a permanent
     * lock.  If that process terminates its exclusive lock is lost,
     * but if we already had the file open we can (probably) lock it
     * even though it has been unlinked.  So we need to insist that
     * it exist.
     */
    if (access(db->lock->filename, F_OK) < 0) {
        (void) krb5_lock_file(db->lock->context,
                              fileno(db->lock->lockfile),
                              KRB5_LOCKMODE_UNLOCK);
        return OSA_ADB_NOLOCKFILE;
    }

    /* we have the shared/exclusive lock */

    if (perm) {
        if (unlink(db->lock->filename) < 0) {
            /* somehow we can't delete the file, but we already */
            /* have the lock, so release it and return */

            ret = errno;
            (void) krb5_lock_file(db->lock->context,
                                  fileno(db->lock->lockfile),
                                  KRB5_LOCKMODE_UNLOCK);

            /* maybe we should return CANTLOCK_DB.. but that would */
            /* look just like the db was already locked */
            return ret;
        }

        /* this releases our exclusive lock.. which is okay because */
        /* now no one else can get one either */
        (void) fclose(db->lock->lockfile);
    }

    db->lock->lockmode = mode;
    db->lock->lockcnt++;
    return OSA_ADB_OK;
}
Beispiel #13
0
/* Initialize dbc by locking and creating the DB.  If the DB already exists,
 * clear it out if dbc->tempdb is set; otherwise return EEXIST. */
static krb5_error_code
ctx_create_db(krb5_context context, krb5_db2_context *dbc)
{
    krb5_error_code retval = 0;
    char *dbname = NULL, *polname = NULL, *plockname = NULL;

    retval = ctx_allfiles(dbc, &dbname, &dbc->db_lf_name, &polname,
                          &plockname);
    if (retval)
        return retval;

    dbc->db_lf_file = open(dbc->db_lf_name, O_CREAT | O_RDWR | O_TRUNC,
                           0600);
    if (dbc->db_lf_file < 0) {
        retval = errno;
        goto cleanup;
    }
    retval = krb5_lock_file(context, dbc->db_lf_file,
                            KRB5_LOCKMODE_EXCLUSIVE | KRB5_LOCKMODE_DONTBLOCK);
    if (retval != 0)
        goto cleanup;
    set_cloexec_fd(dbc->db_lf_file);
    dbc->db_lock_mode = KRB5_LOCKMODE_EXCLUSIVE;
    dbc->db_locks_held = 1;

    if (dbc->tempdb) {
        /* Temporary DBs are locked for their whole lifetime.  Since we have
         * the lock, any remnant files can be safely destroyed. */
        (void) destroy_file(dbname);
        (void) unlink(polname);
        (void) unlink(plockname);
    }

    dbc->db = open_db(dbc, O_RDWR | O_CREAT | O_EXCL, 0600);
    if (dbc->db == NULL) {
        retval = errno;
        goto cleanup;
    }

    /* Create the policy database, initialize a handle to it, and lock it. */
    retval = osa_adb_create_db(polname, plockname, OSA_ADB_POLICY_DB_MAGIC);
    if (retval)
        goto cleanup;
    retval = osa_adb_init_db(&dbc->policy_db, polname, plockname,
                             OSA_ADB_POLICY_DB_MAGIC);
    if (retval)
        goto cleanup;
    retval = osa_adb_get_lock(dbc->policy_db, KRB5_DB_LOCKMODE_EXCLUSIVE);
    if (retval)
        goto cleanup;

    dbc->db_inited = 1;

cleanup:
    if (retval) {
        if (dbc->db != NULL)
            dbc->db->close(dbc->db);
        if (dbc->db_locks_held > 0) {
            (void) krb5_lock_file(context, dbc->db_lf_file,
                                  KRB5_LOCKMODE_UNLOCK);
        }
        if (dbc->db_lf_file >= 0)
            close(dbc->db_lf_file);
        ctx_clear(dbc);
    }
    free(dbname);
    free(polname);
    free(plockname);
    return retval;
}
Beispiel #14
0
krb5_error_code
krb5_unlock_file(krb5_context context, int fd)
{
    return krb5_lock_file(context, fd, KRB5_LOCKMODE_UNLOCK);
}
Beispiel #15
0
/* Open and lock the cache file.  If mode is FCC_OPEN_AND_ERASE, initialize it
 * with a header.  Call with the mutex locked. */
static krb5_error_code
open_cache_file(krb5_context context, krb5_ccache id, int mode)
{
    krb5_os_context os_ctx = &context->os_context;
    krb5_error_code ret;
    fcc_data *data = id->data;
    char fcc_fvno[2];
    uint16_t fcc_flen, fcc_tag, fcc_taglen;
    uint32_t time_offset, usec_offset;
    int f, open_flag, lock_flag, cnt;
    char buf[1024];

    k5_cc_mutex_assert_locked(context, &data->lock);
    invalidate_cache(data);

    if (data->fd != NO_FILE) {
        /* Don't know what state it's in; shut down and start anew. */
        (void)krb5_unlock_file(context, data->fd);
        (void)close(data->fd);
        data->fd = NO_FILE;
    }

    switch (mode) {
    case FCC_OPEN_AND_ERASE:
        unlink(data->filename);
        open_flag = O_CREAT | O_EXCL | O_TRUNC | O_RDWR;
        break;
    case FCC_OPEN_RDWR:
        open_flag = O_RDWR;
        break;
    case FCC_OPEN_RDONLY:
    default:
        open_flag = O_RDONLY;
        break;
    }

    f = THREEPARAMOPEN(data->filename, open_flag | O_BINARY, 0600);
    if (f == NO_FILE) {
        if (errno == ENOENT) {
            ret = KRB5_FCC_NOFILE;
            k5_setmsg(context, ret, _("Credentials cache file '%s' not found"),
                      data->filename);
            return ret;
        } else {
            return interpret_errno(context, errno);
        }
    }
    set_cloexec_fd(f);

    data->mode = mode;

    if (data->mode == FCC_OPEN_RDONLY)
        lock_flag = KRB5_LOCKMODE_SHARED;
    else
        lock_flag = KRB5_LOCKMODE_EXCLUSIVE;
    ret = krb5_lock_file(context, f, lock_flag);
    if (ret) {
        (void)close(f);
        return ret;
    }

    if (mode == FCC_OPEN_AND_ERASE) {
        /* write the version number */
        store_16_be(context->fcc_default_format, fcc_fvno);
        data->version = context->fcc_default_format;
        cnt = write(f, fcc_fvno, 2);
        if (cnt != 2) {
            ret = (cnt == -1) ? interpret_errno(context, errno) : KRB5_CC_IO;
            goto done;
        }
        data->fd = f;

        if (data->version == FVNO_4) {
            /* V4 of the credentials cache format allows for header tags */
            fcc_flen = 0;

            if (os_ctx->os_flags & KRB5_OS_TOFFSET_VALID)
                fcc_flen += 2 + 2 + 4 + 4;

            /* Write header length. */
            ret = store16(context, id, fcc_flen);
            if (ret)
                goto done;

            if (os_ctx->os_flags & KRB5_OS_TOFFSET_VALID) {
                /* Write time offset tag. */
                fcc_tag = FCC_TAG_DELTATIME;
                fcc_taglen = 2 * 4;

                ret = store16(context, id, fcc_tag);
                if (ret)
                    goto done;
                ret = store16(context, id, fcc_taglen);
                if (ret)
                    goto done;
                ret = store32(context, id, os_ctx->time_offset);
                if (ret)
                    goto done;
                ret = store32(context, id, os_ctx->usec_offset);
                if (ret)
                    goto done;
            }
        }
        invalidate_cache(data);
        goto done;
    }

    /* Verify a valid version number is there. */
    invalidate_cache(data);
    if (read(f, fcc_fvno, 2) != 2) {
        ret = KRB5_CC_FORMAT;
        goto done;
    }
    data->version = load_16_be(fcc_fvno);
    if (data->version != FVNO_4 && data->version != FVNO_3 &&
        data->version != FVNO_2 && data->version != FVNO_1) {
        ret = KRB5_CCACHE_BADVNO;
        goto done;
    }

    data->fd = f;

    if (data->version == FVNO_4) {
        if (read16(context, id, &fcc_flen) || fcc_flen > sizeof(buf)) {
            ret = KRB5_CC_FORMAT;
            goto done;
        }

        while (fcc_flen) {
            if (fcc_flen < 2 * 2 || read16(context, id, &fcc_tag) ||
                read16(context, id, &fcc_taglen) ||
                fcc_taglen > fcc_flen - 2 * 2) {
                ret = KRB5_CC_FORMAT;
                goto done;
            }

            switch (fcc_tag) {
            case FCC_TAG_DELTATIME:
                if (fcc_taglen != 2 * 4) {
                    ret = KRB5_CC_FORMAT;
                    goto done;
                }
                if (!(context->library_options & KRB5_LIBOPT_SYNC_KDCTIME) ||
                    (os_ctx->os_flags & KRB5_OS_TOFFSET_VALID)) {
                    if (read_bytes(context, id, buf, fcc_taglen)) {
                        ret = KRB5_CC_FORMAT;
                        goto done;
                    }
                    break;
                }
                if (read32(context, id, NULL, &time_offset) ||
                    read32(context, id, NULL, &usec_offset)) {
                    ret = KRB5_CC_FORMAT;
                    goto done;
                }
                os_ctx->time_offset = time_offset;
                os_ctx->usec_offset = usec_offset;
                os_ctx->os_flags =
                    ((os_ctx->os_flags & ~KRB5_OS_TOFFSET_TIME) |
                     KRB5_OS_TOFFSET_VALID);
                break;
            default:
                if (fcc_taglen && read_bytes(context, id, buf, fcc_taglen)) {
                    ret = KRB5_CC_FORMAT;
                    goto done;
                }
                break;
            }
            fcc_flen -= (2 * 2 + fcc_taglen);
        }
    }

done:
    if (ret) {
        data->fd = -1;
        (void)krb5_unlock_file(context, f);
        (void)close(f);
    }
    return ret;
}