summaryrefslogtreecommitdiffstats
path: root/third_party/heimdal/lib/krb5/keytab_file.c
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/heimdal/lib/krb5/keytab_file.c')
-rw-r--r--third_party/heimdal/lib/krb5/keytab_file.c856
1 files changed, 856 insertions, 0 deletions
diff --git a/third_party/heimdal/lib/krb5/keytab_file.c b/third_party/heimdal/lib/krb5/keytab_file.c
new file mode 100644
index 0000000..61b5d6d
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/keytab_file.c
@@ -0,0 +1,856 @@
+/*
+ * Copyright (c) 1997 - 2017 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+#define KRB5_KT_VNO_1 1
+#define KRB5_KT_VNO_2 2
+#define KRB5_KT_VNO KRB5_KT_VNO_2
+
+#define KRB5_KT_FL_JAVA 1
+
+
+/* file operations -------------------------------------------- */
+
+struct fkt_data {
+ char *filename;
+ int flags;
+};
+
+static krb5_error_code
+krb5_kt_ret_data(krb5_context context,
+ krb5_storage *sp,
+ krb5_data *data)
+{
+ krb5_error_code ret;
+ krb5_ssize_t bytes;
+ int16_t size;
+
+ ret = krb5_ret_int16(sp, &size);
+ if(ret)
+ return ret;
+ data->length = size;
+ data->data = malloc(size);
+ if (data->data == NULL)
+ return krb5_enomem(context);
+ bytes = krb5_storage_read(sp, data->data, size);
+ if (bytes != size)
+ return (bytes == -1) ? errno : KRB5_KT_END;
+ return 0;
+}
+
+static krb5_error_code
+krb5_kt_ret_string(krb5_context context,
+ krb5_storage *sp,
+ heim_general_string *data)
+{
+ krb5_error_code ret;
+ krb5_ssize_t bytes;
+ int16_t size;
+
+ ret = krb5_ret_int16(sp, &size);
+ if(ret)
+ return ret;
+ *data = malloc(size + 1);
+ if (*data == NULL)
+ return krb5_enomem(context);
+ bytes = krb5_storage_read(sp, *data, size);
+ (*data)[size] = '\0';
+ if (bytes != size)
+ return (bytes == -1) ? errno : KRB5_KT_END;
+ return 0;
+}
+
+static krb5_error_code
+krb5_kt_store_data(krb5_context context,
+ krb5_storage *sp,
+ krb5_data data)
+{
+ krb5_error_code ret;
+ krb5_ssize_t bytes;
+
+ ret = krb5_store_int16(sp, data.length);
+ if (ret != 0)
+ return ret;
+ bytes = krb5_storage_write(sp, data.data, data.length);
+ if (bytes != (int)data.length)
+ return bytes == -1 ? errno : KRB5_KT_END;
+ return 0;
+}
+
+static krb5_error_code
+krb5_kt_store_string(krb5_storage *sp,
+ heim_general_string data)
+{
+ krb5_error_code ret;
+ krb5_ssize_t bytes;
+ size_t len = strlen(data);
+
+ ret = krb5_store_int16(sp, len);
+ if (ret != 0)
+ return ret;
+ bytes = krb5_storage_write(sp, data, len);
+ if (bytes != (int)len)
+ return bytes == -1 ? errno : KRB5_KT_END;
+ return 0;
+}
+
+static krb5_error_code
+krb5_kt_ret_keyblock(krb5_context context,
+ struct fkt_data *fkt,
+ krb5_storage *sp,
+ krb5_keyblock *p)
+{
+ int ret;
+ int16_t tmp;
+
+ ret = krb5_ret_int16(sp, &tmp); /* keytype + etype */
+ if(ret) {
+ krb5_set_error_message(context, ret,
+ N_("Cant read keyblock from file %s", ""),
+ fkt->filename);
+ return ret;
+ }
+ p->keytype = tmp;
+ ret = krb5_kt_ret_data(context, sp, &p->keyvalue);
+ if (ret)
+ krb5_set_error_message(context, ret,
+ N_("Cant read keyblock from file %s", ""),
+ fkt->filename);
+ return ret;
+}
+
+static krb5_error_code
+krb5_kt_store_keyblock(krb5_context context,
+ struct fkt_data *fkt,
+ krb5_storage *sp,
+ krb5_keyblock *p)
+{
+ int ret;
+
+ ret = krb5_store_int16(sp, p->keytype); /* keytype + etype */
+ if(ret) {
+ krb5_set_error_message(context, ret,
+ N_("Cant store keyblock to file %s", ""),
+ fkt->filename);
+ return ret;
+ }
+ ret = krb5_kt_store_data(context, sp, p->keyvalue);
+ if (ret)
+ krb5_set_error_message(context, ret,
+ N_("Cant store keyblock to file %s", ""),
+ fkt->filename);
+ return ret;
+}
+
+
+static krb5_error_code
+krb5_kt_ret_principal(krb5_context context,
+ struct fkt_data *fkt,
+ krb5_storage *sp,
+ krb5_principal *princ)
+{
+ size_t i;
+ int ret;
+ krb5_principal p;
+ int16_t len;
+
+ ALLOC(p, 1);
+ if(p == NULL)
+ return krb5_enomem(context);
+
+ ret = krb5_ret_int16(sp, &len);
+ if(ret) {
+ krb5_set_error_message(context, ret,
+ N_("Failed decoding length of "
+ "keytab principal in keytab file %s", ""),
+ fkt->filename);
+ goto out;
+ }
+ if(krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS))
+ len--;
+ if (len < 0) {
+ ret = KRB5_KT_END;
+ krb5_set_error_message(context, ret,
+ N_("Keytab principal contains "
+ "invalid length in keytab %s", ""),
+ fkt->filename);
+ goto out;
+ }
+ ret = krb5_kt_ret_string(context, sp, &p->realm);
+ if(ret) {
+ krb5_set_error_message(context, ret,
+ N_("Can't read realm from keytab: %s", ""),
+ fkt->filename);
+ goto out;
+ }
+ p->name.name_string.val = calloc(len, sizeof(*p->name.name_string.val));
+ if(p->name.name_string.val == NULL) {
+ ret = krb5_enomem(context);
+ goto out;
+ }
+ p->name.name_string.len = len;
+ for(i = 0; i < p->name.name_string.len; i++){
+ ret = krb5_kt_ret_string(context, sp, p->name.name_string.val + i);
+ if(ret) {
+ krb5_set_error_message(context, ret,
+ N_("Can't read principal from "
+ "keytab: %s", ""),
+ fkt->filename);
+ goto out;
+ }
+ }
+ if (krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE))
+ p->name.name_type = KRB5_NT_UNKNOWN;
+ else {
+ int32_t tmp32;
+ ret = krb5_ret_int32(sp, &tmp32);
+ p->name.name_type = tmp32;
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("Can't read name-type from "
+ "keytab: %s", ""),
+ fkt->filename);
+ goto out;
+ }
+ }
+ *princ = p;
+ return 0;
+out:
+ krb5_free_principal(context, p);
+ return ret;
+}
+
+static krb5_error_code
+krb5_kt_store_principal(krb5_context context,
+ krb5_storage *sp,
+ krb5_principal p)
+{
+ size_t i;
+ int ret;
+
+ if(krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS))
+ ret = krb5_store_int16(sp, p->name.name_string.len + 1);
+ else
+ ret = krb5_store_int16(sp, p->name.name_string.len);
+ if(ret) return ret;
+ ret = krb5_kt_store_string(sp, p->realm);
+ if(ret) return ret;
+ for(i = 0; i < p->name.name_string.len; i++){
+ ret = krb5_kt_store_string(sp, p->name.name_string.val[i]);
+ if(ret)
+ return ret;
+ }
+ if(!krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE)) {
+ ret = krb5_store_int32(sp, p->name.name_type);
+ if(ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static krb5_error_code KRB5_CALLCONV
+fkt_resolve(krb5_context context, const char *name, krb5_keytab id)
+{
+ struct fkt_data *d;
+
+ d = malloc(sizeof(*d));
+ if(d == NULL)
+ return krb5_enomem(context);
+ d->filename = strdup(name);
+ if(d->filename == NULL) {
+ free(d);
+ return krb5_enomem(context);
+ }
+ d->flags = 0;
+ id->data = d;
+ return 0;
+}
+
+static krb5_error_code KRB5_CALLCONV
+fkt_resolve_java14(krb5_context context, const char *name, krb5_keytab id)
+{
+ krb5_error_code ret;
+
+ ret = fkt_resolve(context, name, id);
+ if (ret == 0) {
+ struct fkt_data *d = id->data;
+ d->flags |= KRB5_KT_FL_JAVA;
+ }
+ return ret;
+}
+
+static krb5_error_code KRB5_CALLCONV
+fkt_close(krb5_context context, krb5_keytab id)
+{
+ struct fkt_data *d = id->data;
+ free(d->filename);
+ free(d);
+ return 0;
+}
+
+static krb5_error_code KRB5_CALLCONV
+fkt_destroy(krb5_context context, krb5_keytab id)
+{
+ struct fkt_data *d = id->data;
+ _krb5_erase_file(context, d->filename);
+ return 0;
+}
+
+static krb5_error_code KRB5_CALLCONV
+fkt_get_name(krb5_context context,
+ krb5_keytab id,
+ char *name,
+ size_t namesize)
+{
+ /* This function is XXX */
+ struct fkt_data *d = id->data;
+ strlcpy(name, d->filename, namesize);
+ return 0;
+}
+
+static void
+storage_set_flags(krb5_context context, krb5_storage *sp, int vno)
+{
+ int flags = 0;
+ switch(vno) {
+ case KRB5_KT_VNO_1:
+ flags |= KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS;
+ flags |= KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE;
+ flags |= KRB5_STORAGE_HOST_BYTEORDER;
+ break;
+ case KRB5_KT_VNO_2:
+ break;
+ default:
+ krb5_warnx(context,
+ "storage_set_flags called with bad vno (%d)", vno);
+ }
+ krb5_storage_set_flags(sp, flags);
+}
+
+static krb5_error_code
+fkt_start_seq_get_int(krb5_context context,
+ krb5_keytab id,
+ int flags,
+ int exclusive,
+ krb5_kt_cursor *c)
+{
+ int8_t pvno, tag;
+ krb5_error_code ret;
+ struct fkt_data *d = id->data;
+ const char *stdio_mode = "rb";
+
+ memset(c, 0, sizeof(*c));
+ c->fd = open (d->filename, flags);
+ if (c->fd < 0) {
+ ret = errno;
+ krb5_set_error_message(context, ret,
+ N_("keytab %s open failed: %s", ""),
+ d->filename, strerror(ret));
+ return ret;
+ }
+ rk_cloexec(c->fd);
+ ret = _krb5_xlock(context, c->fd, exclusive, d->filename);
+ if (ret) {
+ close(c->fd);
+ return ret;
+ }
+ if ((flags & O_ACCMODE) == O_RDWR && (flags & O_APPEND))
+ stdio_mode = "ab+";
+ else if ((flags & O_ACCMODE) == O_RDWR)
+ stdio_mode = "rb+";
+ else if ((flags & O_ACCMODE) == O_WRONLY)
+ stdio_mode = "wb";
+ c->sp = krb5_storage_stdio_from_fd(c->fd, stdio_mode);
+ if (c->sp == NULL) {
+ close(c->fd);
+ return krb5_enomem(context);
+ }
+ krb5_storage_set_eof_code(c->sp, KRB5_KT_END);
+ ret = krb5_ret_int8(c->sp, &pvno);
+ if(ret) {
+ krb5_storage_free(c->sp);
+ close(c->fd);
+ krb5_clear_error_message(context);
+ return ret;
+ }
+ if(pvno != 5) {
+ krb5_storage_free(c->sp);
+ close(c->fd);
+ krb5_clear_error_message (context);
+ return KRB5_KEYTAB_BADVNO;
+ }
+ ret = krb5_ret_int8(c->sp, &tag);
+ if (ret) {
+ krb5_storage_free(c->sp);
+ close(c->fd);
+ krb5_clear_error_message(context);
+ return ret;
+ }
+ id->version = tag;
+ storage_set_flags(context, c->sp, id->version);
+ return 0;
+}
+
+static krb5_error_code KRB5_CALLCONV
+fkt_start_seq_get(krb5_context context,
+ krb5_keytab id,
+ krb5_kt_cursor *c)
+{
+ return fkt_start_seq_get_int(context, id, O_RDONLY | O_BINARY | O_CLOEXEC, 0, c);
+}
+
+static krb5_error_code
+fkt_next_entry_int(krb5_context context,
+ krb5_keytab id,
+ krb5_keytab_entry *entry,
+ krb5_kt_cursor *cursor,
+ off_t *start,
+ off_t *end)
+{
+ struct fkt_data *d = id->data;
+ int32_t len;
+ int ret;
+ int8_t tmp8;
+ int32_t tmp32;
+ uint32_t utmp32;
+ off_t pos, curpos;
+
+ pos = krb5_storage_seek(cursor->sp, 0, SEEK_CUR);
+loop:
+ ret = krb5_ret_int32(cursor->sp, &len);
+ if (ret)
+ return ret;
+ if(len < 0) {
+ pos = krb5_storage_seek(cursor->sp, -len, SEEK_CUR);
+ goto loop;
+ }
+ ret = krb5_kt_ret_principal (context, d, cursor->sp, &entry->principal);
+ if (ret)
+ goto out;
+ ret = krb5_ret_uint32(cursor->sp, &utmp32);
+ entry->timestamp = utmp32;
+ if (ret)
+ goto out;
+ ret = krb5_ret_int8(cursor->sp, &tmp8);
+ if (ret)
+ goto out;
+ entry->vno = tmp8;
+ ret = krb5_kt_ret_keyblock (context, d, cursor->sp, &entry->keyblock);
+ if (ret)
+ goto out;
+ /* there might be a 32 bit kvno here
+ * if it's zero, assume that the 8bit one was right,
+ * otherwise trust the new value */
+ curpos = krb5_storage_seek(cursor->sp, 0, SEEK_CUR);
+ if(len + 4 + pos - curpos >= 4) {
+ ret = krb5_ret_int32(cursor->sp, &tmp32);
+ if (ret == 0 && tmp32 != 0)
+ entry->vno = tmp32;
+ }
+ /* there might be a flags field here */
+ if(len + 4 + pos - curpos >= 8) {
+ ret = krb5_ret_uint32(cursor->sp, &utmp32);
+ if (ret == 0)
+ entry->flags = utmp32;
+ } else
+ entry->flags = 0;
+
+ entry->aliases = NULL;
+
+ if(start) *start = pos;
+ if(end) *end = pos + 4 + len;
+ out:
+ if (ret)
+ krb5_kt_free_entry(context, entry);
+ krb5_storage_seek(cursor->sp, pos + 4 + len, SEEK_SET);
+ return ret;
+}
+
+static krb5_error_code KRB5_CALLCONV
+fkt_next_entry(krb5_context context,
+ krb5_keytab id,
+ krb5_keytab_entry *entry,
+ krb5_kt_cursor *cursor)
+{
+ return fkt_next_entry_int(context, id, entry, cursor, NULL, NULL);
+}
+
+static krb5_error_code KRB5_CALLCONV
+fkt_end_seq_get(krb5_context context,
+ krb5_keytab id,
+ krb5_kt_cursor *cursor)
+{
+ krb5_storage_free(cursor->sp);
+ close(cursor->fd);
+ return 0;
+}
+
+static krb5_error_code KRB5_CALLCONV
+fkt_setup_keytab(krb5_context context,
+ krb5_keytab id,
+ krb5_storage *sp)
+{
+ krb5_error_code ret;
+ ret = krb5_store_int8(sp, 5);
+ if(ret)
+ return ret;
+ if(id->version == 0)
+ id->version = KRB5_KT_VNO;
+ return krb5_store_int8 (sp, id->version);
+}
+
+static krb5_error_code KRB5_CALLCONV
+fkt_add_entry(krb5_context context,
+ krb5_keytab id,
+ krb5_keytab_entry *entry)
+{
+ int ret;
+ int fd;
+ krb5_storage *sp;
+ krb5_ssize_t bytes;
+ struct fkt_data *d = id->data;
+ krb5_data keytab;
+ int32_t len;
+
+ fd = open(d->filename, O_RDWR | O_BINARY | O_CLOEXEC);
+ if (fd < 0) {
+ fd = open(d->filename, O_RDWR | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC, 0600);
+ if (fd < 0) {
+ ret = errno;
+ krb5_set_error_message(context, ret,
+ N_("open(%s): %s", ""), d->filename,
+ strerror(ret));
+ return ret;
+ }
+ rk_cloexec(fd);
+
+ ret = _krb5_xlock(context, fd, 1, d->filename);
+ if (ret) {
+ close(fd);
+ return ret;
+ }
+ sp = krb5_storage_stdio_from_fd(fd, "wb+");
+ if (sp == NULL) {
+ close(fd);
+ return krb5_enomem(context);
+ }
+ krb5_storage_set_eof_code(sp, KRB5_KT_END);
+ ret = fkt_setup_keytab(context, id, sp);
+ if (ret) {
+ goto out;
+ }
+ storage_set_flags(context, sp, id->version);
+ } else {
+ int8_t pvno, tag;
+
+ rk_cloexec(fd);
+
+ ret = _krb5_xlock(context, fd, 1, d->filename);
+ if (ret) {
+ close(fd);
+ return ret;
+ }
+ sp = krb5_storage_stdio_from_fd(fd, "wb+");
+ if (sp == NULL) {
+ (void) close(fd);
+ return ret;
+ }
+ krb5_storage_set_eof_code(sp, KRB5_KT_END);
+ ret = krb5_ret_int8(sp, &pvno);
+ if(ret) {
+ /* we probably have a zero byte file, so try to set it up
+ properly */
+ ret = fkt_setup_keytab(context, id, sp);
+ if(ret) {
+ krb5_set_error_message(context, ret,
+ N_("%s: keytab is corrupted: %s", ""),
+ d->filename, strerror(ret));
+ goto out;
+ }
+ storage_set_flags(context, sp, id->version);
+ } else {
+ if(pvno != 5) {
+ ret = KRB5_KEYTAB_BADVNO;
+ krb5_set_error_message(context, ret,
+ N_("Bad version in keytab %s", ""),
+ d->filename);
+ goto out;
+ }
+ ret = krb5_ret_int8 (sp, &tag);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("failed reading tag from "
+ "keytab %s", ""),
+ d->filename);
+ goto out;
+ }
+ id->version = tag;
+ storage_set_flags(context, sp, id->version);
+ }
+ }
+
+ {
+ krb5_storage *emem;
+ emem = krb5_storage_emem();
+ if(emem == NULL) {
+ ret = krb5_enomem(context);
+ goto out;
+ }
+ ret = krb5_kt_store_principal(context, emem, entry->principal);
+ if(ret) {
+ krb5_set_error_message(context, ret,
+ N_("Failed storing principal "
+ "in keytab %s", ""),
+ d->filename);
+ krb5_storage_free(emem);
+ goto out;
+ }
+ ret = krb5_store_int32 (emem, entry->timestamp);
+ if(ret) {
+ krb5_set_error_message(context, ret,
+ N_("Failed storing timpstamp "
+ "in keytab %s", ""),
+ d->filename);
+ krb5_storage_free(emem);
+ goto out;
+ }
+ ret = krb5_store_int8 (emem, entry->vno % 256);
+ if(ret) {
+ krb5_set_error_message(context, ret,
+ N_("Failed storing kvno "
+ "in keytab %s", ""),
+ d->filename);
+ krb5_storage_free(emem);
+ goto out;
+ }
+ ret = krb5_kt_store_keyblock (context, d, emem, &entry->keyblock);
+ if(ret) {
+ krb5_storage_free(emem);
+ goto out;
+ }
+ if ((d->flags & KRB5_KT_FL_JAVA) == 0) {
+ ret = krb5_store_int32 (emem, entry->vno);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("Failed storing extended kvno "
+ "in keytab %s", ""),
+ d->filename);
+ krb5_storage_free(emem);
+ goto out;
+ }
+ ret = krb5_store_uint32 (emem, entry->flags);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("Failed storing extended kvno "
+ "in keytab %s", ""),
+ d->filename);
+ krb5_storage_free(emem);
+ goto out;
+ }
+ }
+
+ ret = krb5_storage_to_data(emem, &keytab);
+ krb5_storage_free(emem);
+ if(ret) {
+ krb5_set_error_message(context, ret,
+ N_("Failed converting keytab entry "
+ "to memory block for keytab %s", ""),
+ d->filename);
+ goto out;
+ }
+ }
+
+ while(1) {
+ off_t here;
+
+ here = krb5_storage_seek(sp, 0, SEEK_CUR);
+ if (here == -1) {
+ ret = errno;
+ krb5_set_error_message(context, ret,
+ N_("Failed writing keytab block "
+ "in keytab %s: %s", ""),
+ d->filename, strerror(ret));
+ goto out;
+ }
+ ret = krb5_ret_int32(sp, &len);
+ if (ret) {
+ /* There could have been a partial length. Recover! */
+ (void) krb5_storage_truncate(sp, here);
+ len = keytab.length;
+ break;
+ }
+ if(len < 0) {
+ len = -len;
+ if(len >= (int)keytab.length) {
+ krb5_storage_seek(sp, -4, SEEK_CUR);
+ break;
+ }
+ }
+ krb5_storage_seek(sp, len, SEEK_CUR);
+ }
+ ret = krb5_store_int32(sp, len);
+ if (ret != 0)
+ goto out;
+ bytes = krb5_storage_write(sp, keytab.data, keytab.length);
+ if (bytes != keytab.length) {
+ ret = bytes == -1 ? errno : KRB5_KT_END;
+ krb5_set_error_message(context, ret,
+ N_("Failed writing keytab block "
+ "in keytab %s: %s", ""),
+ d->filename, strerror(ret));
+ }
+ memset(keytab.data, 0, keytab.length);
+ krb5_data_free(&keytab);
+ out:
+ if (ret == 0)
+ ret = krb5_storage_fsync(sp);
+ krb5_storage_free(sp);
+ close(fd);
+ return ret;
+}
+
+static krb5_error_code KRB5_CALLCONV
+fkt_remove_entry(krb5_context context,
+ krb5_keytab id,
+ krb5_keytab_entry *entry)
+{
+ struct fkt_data *fkt = id->data;
+ krb5_ssize_t bytes;
+ krb5_keytab_entry e;
+ krb5_kt_cursor cursor;
+ off_t pos_start, pos_end;
+ int found = 0;
+ krb5_error_code ret;
+
+ ret = fkt_start_seq_get_int(context, id, O_RDWR | O_BINARY | O_CLOEXEC, 1, &cursor);
+ if (ret != 0) {
+ const char *emsg = krb5_get_error_message(context, ret);
+
+ krb5_set_error_message(context, ret,
+ N_("Could not open keytab file for write: %s: %s", ""),
+ fkt->filename,
+ emsg);
+ krb5_free_error_message(context, emsg);
+ return ret;
+ }
+ while (ret == 0 &&
+ (ret = fkt_next_entry_int(context, id, &e, &cursor,
+ &pos_start, &pos_end)) == 0) {
+ if (krb5_kt_compare(context, &e, entry->principal,
+ entry->vno, entry->keyblock.keytype)) {
+ int32_t len;
+ unsigned char buf[128];
+ found = 1;
+ krb5_storage_seek(cursor.sp, pos_start, SEEK_SET);
+ len = pos_end - pos_start - 4;
+ ret = krb5_store_int32(cursor.sp, -len);
+ memset(buf, 0, sizeof(buf));
+ while (ret == 0 && len > 0) {
+ bytes = krb5_storage_write(cursor.sp, buf,
+ min((size_t)len, sizeof(buf)));
+ if (bytes != min((size_t)len, sizeof(buf))) {
+ ret = bytes == -1 ? errno : KRB5_KT_END;
+ break;
+ }
+ len -= min((size_t)len, sizeof(buf));
+ }
+ }
+ krb5_kt_free_entry(context, &e);
+ }
+ (void) krb5_kt_end_seq_get(context, id, &cursor);
+ if (ret == KRB5_KT_END)
+ ret = 0;
+ if (ret) {
+ const char *emsg = krb5_get_error_message(context, ret);
+
+ krb5_set_error_message(context, ret,
+ N_("Could not remove keytab entry from %s: %s", ""),
+ fkt->filename,
+ emsg);
+ krb5_free_error_message(context, emsg);
+ } else if (!found) {
+ krb5_clear_error_message(context);
+ return KRB5_KT_NOTFOUND;
+ }
+ return ret;
+}
+
+const krb5_kt_ops krb5_fkt_ops = {
+ "FILE",
+ fkt_resolve,
+ fkt_get_name,
+ fkt_close,
+ fkt_destroy,
+ NULL, /* get */
+ fkt_start_seq_get,
+ fkt_next_entry,
+ fkt_end_seq_get,
+ fkt_add_entry,
+ fkt_remove_entry,
+ NULL,
+ 0
+};
+
+const krb5_kt_ops krb5_wrfkt_ops = {
+ "WRFILE",
+ fkt_resolve,
+ fkt_get_name,
+ fkt_close,
+ fkt_destroy,
+ NULL, /* get */
+ fkt_start_seq_get,
+ fkt_next_entry,
+ fkt_end_seq_get,
+ fkt_add_entry,
+ fkt_remove_entry,
+ NULL,
+ 0
+};
+
+const krb5_kt_ops krb5_javakt_ops = {
+ "JAVA14",
+ fkt_resolve_java14,
+ fkt_get_name,
+ fkt_close,
+ fkt_destroy,
+ NULL, /* get */
+ fkt_start_seq_get,
+ fkt_next_entry,
+ fkt_end_seq_get,
+ fkt_add_entry,
+ fkt_remove_entry,
+ NULL,
+ 0
+};