First cut of ktutil; change functions have not yet been added
authorTom Yu <tlyu@mit.edu>
Fri, 18 Aug 1995 02:43:55 +0000 (02:43 +0000)
committerTom Yu <tlyu@mit.edu>
Fri, 18 Aug 1995 02:43:55 +0000 (02:43 +0000)
git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@6547 dc483132-0cff-0310-8789-dd5450dbe970

src/kadmin/ktutil/.cvsignore [new file with mode: 0644]
src/kadmin/ktutil/ChangeLog [new file with mode: 0644]
src/kadmin/ktutil/Makefile.in [new file with mode: 0644]
src/kadmin/ktutil/configure.in [new file with mode: 0644]
src/kadmin/ktutil/ktutil.c [new file with mode: 0644]
src/kadmin/ktutil/ktutil.h [new file with mode: 0644]
src/kadmin/ktutil/ktutil_ct.ct [new file with mode: 0644]
src/kadmin/ktutil/ktutil_funcs.c [new file with mode: 0644]

diff --git a/src/kadmin/ktutil/.cvsignore b/src/kadmin/ktutil/.cvsignore
new file mode 100644 (file)
index 0000000..e8c05a6
--- /dev/null
@@ -0,0 +1 @@
+configure
diff --git a/src/kadmin/ktutil/ChangeLog b/src/kadmin/ktutil/ChangeLog
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/kadmin/ktutil/Makefile.in b/src/kadmin/ktutil/Makefile.in
new file mode 100644 (file)
index 0000000..d59ad07
--- /dev/null
@@ -0,0 +1,32 @@
+CFLAGS = $(CCOPTS) $(DEFS) $(LOCALINCLUDE)
+
+all::
+
+OBJS=  ktutil.o \
+       ktutil_ct.o \
+       ktutil_funcs.o
+
+SRCS=  $(srcdir)/ktutil.c \
+       $(srcdir)/ktutil_ct.c \
+       $(srcdir)/ktutil_funcs.c
+
+all:: ktutil
+
+ktutil: ktutil.o $(OBJS) $(DEPLIBS)
+       $(LD) $(LDFLAGS) $(LDARGS) -o ktutil $(OBJS) $(LIBS)
+
+install::
+       $(INSTALL_PROGRAM) ktutil ${DESTDIR}$(ADMIN_BINDIR)/ktutil
+
+# needed until we run makedepend
+ktutil_ct.c: ktutil_ct.ct
+
+ktutil_ct.o: ktutil_ct.c
+
+clean::
+       $(RM) ktutil_ct.c
+
+depend:: ktutil_ct.c
+
+clean::
+       $(RM) ktutil
diff --git a/src/kadmin/ktutil/configure.in b/src/kadmin/ktutil/configure.in
new file mode 100644 (file)
index 0000000..6797af1
--- /dev/null
@@ -0,0 +1,10 @@
+AC_INIT(ktutil.c)
+CONFIG_RULES
+AC_PROG_INSTALL
+SS_RULES
+USE_KADM_LIBRARY
+USE_KRB4_LIBRARY
+USE_SS_LIBRARY
+KRB5_LIBRARIES
+V5_USE_SHARED_LIB
+V5_AC_OUTPUT_MAKEFILE
diff --git a/src/kadmin/ktutil/ktutil.c b/src/kadmin/ktutil/ktutil.c
new file mode 100644 (file)
index 0000000..ae886ba
--- /dev/null
@@ -0,0 +1,225 @@
+/*
+ * kadmin/ktutil/ktutil.c
+ *
+ * Copyright 1995 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ * 
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ * 
+ * SS user interface for ktutil.
+ */
+
+#include "k5-int.h"
+#include "ktutil.h"
+#include <com_err.h>
+#include <ss/ss.h>
+#include <stdio.h>
+#ifdef HAS_STDLIB_H
+#include <stdlib.h>
+#endif
+
+extern ss_request_table ktutil_cmds;
+krb5_context kcontext;
+krb5_kt_list ktlist = NULL;
+
+static char *Month_names[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+                              "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+
+int main(argc, argv)
+    int argc;
+    char *argv[];
+{
+    krb5_error_code retval;
+    extern krb5_kt_ops krb5_ktf_writable_ops;
+    int sci_idx;
+
+    krb5_init_context(&kcontext);
+    krb5_init_ets(kcontext);
+    retval = krb5_kt_register(kcontext, &krb5_ktf_writable_ops);
+    if (retval) {
+       com_err(argv[0], retval,
+               "while registering writable key table functions");
+       exit(1);
+    }
+    sci_idx = ss_create_invocation("ktutil", "5.0", (char *)NULL,
+                                  &ktutil_cmds, &retval);
+    if (retval) {
+       ss_perror(sci_idx, retval, "creating invocation");
+       exit(1);
+    }
+    ss_listen(sci_idx, &retval);
+    ktutil_free_kt_list(kcontext, ktlist);
+    exit(0);
+}
+
+void ktutil_clear_list(argc, argv)
+    int argc;
+    char *argv[];
+{
+    krb5_error_code retval;
+
+    if (argc != 1) {
+       fprintf(stderr, "%s: invalid arguments\n", argv[0]);
+       return;
+    }
+    retval = ktutil_free_kt_list(kcontext, ktlist);
+    if (retval)
+       com_err(argv[0], retval, "while freeing ktlist");
+    ktlist = NULL;
+}
+
+void ktutil_read_v5(argc, argv)
+    int argc;
+    char *argv[];
+{
+    krb5_error_code retval;
+
+    if (argc != 2) {
+       fprintf(stderr, "%s: must specify keytab to read\n", argv[0]);
+       return;
+    }
+    retval = ktutil_read_keytab(kcontext, argv[1], &ktlist);
+    if (retval)
+       com_err(argv[0], retval, "while reading keytab \"%s\"", argv[1]);
+}
+
+void ktutil_read_v4(argc, argv)
+    int argc;
+    char *argv[];
+{
+#ifdef KRB5_KRB4_COMPAT
+    krb5_error_code retval;
+
+    if (argc != 2) {
+       fprintf(stderr, "%s: must specify the srvtab to read\n", argv[0]);
+       return;
+    }
+    retval = ktutil_read_srvtab(kcontext, argv[1], &ktlist);
+    if (retval)
+       com_err(argv[0], retval, "while reading srvtab \"%s\"", argv[1]);
+#else
+    fprintf(stderr, "%s: krb4 support not configured\n", argv[0]);
+#endif
+}
+
+void ktutil_write_v5(argc, argv)
+    int argc;
+    char *argv[];
+{
+    krb5_error_code retval;
+
+    if (argc != 2) {
+       fprintf(stderr, "%s: must specify keytab to write\n", argv[0]);
+       return;
+    }
+    retval = ktutil_write_keytab(kcontext, ktlist, argv[1]);
+    if (retval)
+       com_err(argv[0], retval, "while writing keytab \"%s\"", argv[1]);
+}
+
+void ktutil_write_v4(argc, argv)
+    int argc;
+    char *argv[];
+{
+#ifdef KRB5_KRB4_COMPAT
+    krb5_error_code retval;
+
+    if (argc != 2) {
+       fprintf(stderr, "%s: must specify srvtab to write\n", argv[0]);
+       return;
+    }
+    retval = ktutil_write_keytab(kcontext, ktlist, argv[1]);
+    if (retval)
+       com_err(argv[0], retval, "while writing srvtab \"%s\"", argv[1]);
+#else
+    fprintf(stderr, "%s: krb4 support not configured\n", argv[0]);
+#endif
+}
+
+void ktutil_delete_entry(argc, argv)
+    int argc;
+    char *argv[];
+{
+    krb5_error_code retval;
+
+    if (argc != 2) {
+       fprintf(stderr, "%s: must specify entry to delete\n", argv[0]);
+       return;
+    }
+    retval = ktutil_delete(kcontext, &ktlist, atoi(argv[1]));
+    if (retval)
+       com_err(argv[0], retval, "while deleting entry %d", atoi(argv[1]));
+}
+
+void ktutil_list(argc, argv)
+    int argc;
+    char *argv[];
+{
+    krb5_error_code retval;
+    krb5_kt_list lp;
+    struct tm *stime;
+    int show_time = 0, show_keys = 0;
+    int i, j;
+    char *pname;
+
+    for (i = 1; i < argc; i++) {
+       if ((strlen(argv[i]) == 2) && !strncmp(argv[i], "-t", 2)) {
+           show_time++;
+           continue;
+       }
+       if ((strlen(argv[i]) == 2) && !strncmp(argv[i], "-k", 2)) {
+           show_keys++;
+           continue;
+       }
+       fprintf(stderr, "%s: illegal arguments\n", argv[0]);
+       return;
+    }
+    if (show_time) {
+       printf("slot KVNO Timestamp          Principal\n");
+       printf("---- ---- ------------------ -------------------------------------------------------\n");
+    } else {
+       printf("slot KVNO Principal\n");
+       printf("---- ---- --------------------------------------------------------------------------\n");
+    }
+    for (i = 1, lp = ktlist; lp; i++, lp = lp->next) {
+       retval = krb5_unparse_name(kcontext, lp->entry->principal, &pname);
+       if (retval) {
+           com_err(argv[0], retval, "while unparsing principal name");
+           return;
+       }
+       printf("%4d %4d ", i, lp->entry->vno);
+       if (show_time) {
+           stime = localtime((time_t *)&lp->entry->timestamp);
+           printf("%2d-%s-%2d %02d:%02d:%02d ",
+                  stime->tm_mday,
+                  Month_names[stime->tm_mon],
+                  stime->tm_year,
+                  stime->tm_hour,
+                  stime->tm_min,
+                  stime->tm_sec);
+       }
+       printf("%40s", pname);
+       if (show_keys) {
+           printf(" (0x");
+           for (j = 0; j < lp->entry->key.length; j++)
+               printf("%02x", lp->entry->key.contents[j]);
+           printf(")");
+       }
+       printf("\n");
+       krb5_xfree(pname);
+    }
+}
diff --git a/src/kadmin/ktutil/ktutil.h b/src/kadmin/ktutil/ktutil.h
new file mode 100644 (file)
index 0000000..0f14def
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * kadmin/ktutil/ktutil.h
+ *
+ * Copyright 1995 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ * 
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ * 
+ */
+
+typedef struct _krb5_kt_list {
+    struct _krb5_kt_list *next;
+    krb5_keytab_entry *entry;
+} *krb5_kt_list;
+
+krb5_error_code ktutil_free_kt_list
+       KRB5_PROTOTYPE((krb5_context,
+                       krb5_kt_list));
+
+krb5_error_code ktutil_delete
+       KRB5_PROTOTYPE((krb5_context,
+                       krb5_kt_list *,
+                       int));
+
+krb5_error_code ktutil_read_keytab
+       KRB5_PROTOTYPE((krb5_context,
+                       char *,
+                       krb5_kt_list *));
+
+krb5_error_code ktutil_write_keytab
+       KRB5_PROTOTYPE((krb5_context,
+                       krb5_kt_list,
+                       char *));
+
+#ifdef KRB5_KRB4_COMPAT
+krb5_error_code ktutil_read_srvtab
+       KRB5_PROTOTYPE((krb5_context,
+                       char *,
+                       krb5_kt_list *));
+krb5_error_code ktutil_write_srvtab
+       KRB5_PROTOTYPE((krb5_context,
+                       krb5_kt_list,
+                       char *));
+#endif
diff --git a/src/kadmin/ktutil/ktutil_ct.ct b/src/kadmin/ktutil/ktutil_ct.ct
new file mode 100644 (file)
index 0000000..1f0269f
--- /dev/null
@@ -0,0 +1,51 @@
+# Copyright 1995 by the Massachusetts Institute of Technology.
+# All Rights Reserved.
+# 
+# Export of this software from the United States of America may
+#   require a specific license from the United States Government.
+#   It is the responsibility of any person or organization contemplating
+#   export to obtain such a license before exporting.
+# 
+# WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+# distribute this software and its documentation for any purpose and
+# without fee is hereby granted, provided that the above copyright
+# notice appear in all copies and that both that copyright notice and
+# this permission notice appear in supporting documentation, and that
+# the name of M.I.T. not be used in advertising or publicity pertaining
+# to distribution of the software without specific, written prior
+# permission.  M.I.T. makes no representations about the suitability of
+# this software for any purpose.  It is provided "as is" without express
+# or implied warranty.
+# 
+# 
+# Command table for ktutil
+#
+
+command_table ktutil_cmds;
+
+request ktutil_clear_list, "Clear the current keylist.",
+       clear_list, clear;
+
+request ktutil_read_v5, "Read a krb5 keytab into the current keylist.",
+       read_kt, rkt;
+
+request ktutil_read_v4, "Read a krb4 srvtab into the current keylist.",
+       read_st, rst;
+
+request ktutil_write_v5, "Write the current keylist to a krb5 keytab.",
+       write_kt, wkt;
+
+request ktutil_write_v4, "Write the current keylist to a krb4 srvtab.",
+       write_st, wst;
+
+request ktutil_delete_entry, "Delete an entry from the current keylist.",
+       delete_entry, delent;
+
+request ktutil_list, "List the current keylist.",
+       list, l;
+
+request ss_list_requests, "List available requests.",
+       list_requests, lr, "?";
+
+request ss_quit, "Exit program.",
+       quit, exit, q;
diff --git a/src/kadmin/ktutil/ktutil_funcs.c b/src/kadmin/ktutil/ktutil_funcs.c
new file mode 100644 (file)
index 0000000..2702c84
--- /dev/null
@@ -0,0 +1,382 @@
+/*
+ * kadmin/ktutil/ktutil_funcs.c
+ *
+ * Copyright 1995 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ * 
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ * 
+ * Utility functions for ktutil.
+ */
+
+#include "k5-int.h"
+#include "ktutil.h"
+#ifdef KRB5_KRB4_COMPAT
+#include "kerberosIV/krb.h"
+#include <stdio.h>
+#endif
+#include <string.h>
+
+/*
+ * Free a kt_list
+ */
+krb5_error_code ktutil_free_kt_list(context, list)
+    krb5_context context;
+    krb5_kt_list list;
+{
+    krb5_kt_list lp, prev;
+    krb5_error_code retval = 0;
+
+    for (lp = list; lp;) {
+       retval = krb5_kt_free_entry(context, lp->entry);
+       krb5_xfree(lp->entry);
+       if (retval)
+           break;
+       prev = lp;
+       lp = lp->next;
+       krb5_xfree(prev);
+    }
+    return retval;
+}
+
+/*
+ * Delete a numbered entry in a kt_list.  Takes a pointer to a kt_list
+ * in case head gets deleted.
+ */
+krb5_error_code ktutil_delete(context, list, index)
+    krb5_context context;
+    krb5_kt_list *list;
+    int index;
+{
+    krb5_kt_list lp, prev;
+    int i;
+
+    for (lp = *list, i = 1; lp; prev = lp, lp = lp->next, i++) {
+       if (i == index) {
+           if (i == 1)
+               *list = lp->next;
+           else
+               prev->next = lp->next;
+           lp->next = NULL;
+           return ktutil_free_kt_list(context, lp);
+       }
+    }
+    return EINVAL;
+}
+
+/*
+ * Read in a keytab and append it to list.  If list starts as NULL,
+ * allocate a new one if necessary.
+ */
+krb5_error_code ktutil_read_keytab(context, name, list)
+    krb5_context context;
+    char *name;
+    krb5_kt_list *list;
+{
+    krb5_kt_list lp = NULL, tail = NULL, back = NULL;
+    krb5_keytab kt;
+    krb5_keytab_entry *entry;
+    krb5_kt_cursor cursor;
+    krb5_error_code retval = 0;
+
+    if (*list) {
+       /* point lp at the tail of the list */
+       for (lp = *list; lp->next; lp = lp->next);
+       back = lp;
+    }
+    retval = krb5_kt_resolve(context, name, &kt);
+    if (retval)
+       return retval;
+    retval = krb5_kt_start_seq_get(context, kt, &cursor);
+    if (retval)
+       goto close_kt;
+    for (;;) {
+       entry = (krb5_keytab_entry *)malloc(sizeof (krb5_keytab_entry));
+       if (!entry) {
+           retval = ENOMEM;
+           break;
+       }
+       memset((char *)entry, 0, sizeof (*entry));
+       retval = krb5_kt_next_entry(context, kt, entry, &cursor);
+       if (retval)
+           break;
+       if (!lp) {              /* if list is empty, start one */
+           lp = (krb5_kt_list)malloc(sizeof (*lp));
+           if (!lp) {
+               retval = ENOMEM;
+               break;
+           }
+       } else {
+           lp->next = (krb5_kt_list)malloc(sizeof (*lp));
+           if (!lp->next) {
+               retval = ENOMEM;
+               break;
+           }
+           lp = lp->next;
+       }
+       if (!tail)
+           tail = lp;
+       lp->next = NULL;
+       lp->entry = entry;
+    }
+    if (entry)
+       krb5_xfree(entry);
+    if (retval)
+       if (retval == KRB5_KT_END)
+           retval = 0;
+       else {
+           ktutil_free_kt_list(context, tail);
+           tail = NULL;
+           if (back)
+               back->next = NULL;
+       }
+    if (!*list)
+       *list = tail;
+    krb5_kt_end_seq_get(context, kt, &cursor);
+ close_kt:
+    krb5_kt_close(context, kt);
+    return retval;
+}
+
+/*
+ * Takes a kt_list and writes it to the named keytab.
+ */
+krb5_error_code ktutil_write_keytab(context, list, name)
+    krb5_context context;
+    krb5_kt_list list;
+    char *name;
+{
+    krb5_kt_list lp;
+    krb5_keytab kt;
+    char ktname[MAXPATHLEN+sizeof("WRFILE:")+1];
+    krb5_error_code retval = 0;
+
+    strcpy(ktname, "WRFILE:");
+    strncat(ktname, name, MAXPATHLEN);
+    retval = krb5_kt_resolve(context, ktname, &kt);
+    if (retval)
+       return retval;
+    for (lp = list; lp; lp = lp->next) {
+       retval = krb5_kt_add_entry(context, kt, lp->entry);
+       if (retval)
+           break;
+    }
+    krb5_kt_close(context, kt);
+    return retval;
+}
+
+#ifdef KRB5_KRB4_COMPAT
+/*
+ * getst() takes a file pointer, a string and a count.  It reads from
+ * the file until either it has read "count" characters, or until it
+ * reads a null byte.  When finished, what has been read exists in the
+ * given string "s".  If "count" characters were actually read, the
+ * last is changed to a null, so the returned string is always null-
+ * terminated.  getst() returns the number of characters read,
+ * including the null terminator.
+ */
+
+int getst(fp, s, n)
+    FILE *fp;
+    register char *s;
+    int n;
+{
+    register count = n;
+    while (fread(s, 1, 1, fp) > 0 && --count)
+        if (*s++ == '\0')
+            return (n - count);
+    *s = '\0';
+    return (n - count);
+}
+
+/*
+ * Read in a named krb4 srvtab and append to list.  Allocate new list
+ * if needed.
+ */
+krb5_error_code ktutil_read_srvtab(context, name, list)
+    krb5_context context;
+    char *name;
+    krb5_kt_list *list;
+{
+    krb5_kt_list lp = NULL, tail = NULL, back = NULL;
+    krb5_keytab_entry *entry;
+    krb5_error_code retval = 0;
+    char sname[SNAME_SZ];      /* name of service */
+    char sinst[INST_SZ];       /* instance of service */
+    char srealm[REALM_SZ];     /* realm of service */
+    unsigned char kvno;                /* key version number */
+    des_cblock key;
+    FILE *fp;
+
+    if (*list) {
+       /* point lp at the tail of the list */
+       for (lp = *list; lp->next; lp = lp->next);
+       back = lp;
+    }
+    fp = fopen(name, "r");
+    if (!fp)
+       return EIO;
+    for (;;) {
+       entry = (krb5_keytab_entry *)malloc(sizeof (krb5_keytab_entry));
+       if (!entry) {
+           retval = ENOMEM;
+           break;
+       }
+       memset((char *)entry, 0, sizeof (*entry));
+       memset(sname, 0, sizeof (sname));
+       memset(sinst, 0, sizeof (sinst));
+       memset(srealm, 0, sizeof (srealm));
+       if (!(getst(fp, sname, SNAME_SZ) > 0 &&
+             getst(fp, sinst, INST_SZ) > 0 &&
+             getst(fp, srealm, REALM_SZ) > 0 &&
+             fread(&kvno, 1, 1, fp) > 0 &&
+             fread((char *)key, sizeof (key), 1, fp) > 0))
+           break;
+       entry->magic = KV5M_KEYTAB_ENTRY;
+       entry->timestamp = 0;   /* XXX */
+       entry->vno = kvno;
+       retval = krb5_425_conv_principal(context,
+                                        sname, sinst, srealm,
+                                        &entry->principal);
+       if (retval)
+           break;
+       entry->key.magic = KV5M_KEYBLOCK;
+       entry->key.etype = ETYPE_UNKNOWN;
+       entry->key.keytype = KEYTYPE_DES;
+       entry->key.length = sizeof (key);
+       entry->key.contents = (krb5_octet *)malloc(sizeof (key));
+       if (!entry->key.contents) {
+           retval = ENOMEM;
+           break;
+       }
+       memcpy((char *)entry->key.contents, (char *)key, sizeof (key));
+       if (!lp) {              /* if list is empty, start one */
+           lp = (krb5_kt_list)malloc(sizeof (*lp));
+           if (!lp) {
+               retval = ENOMEM;
+               break;
+           }
+       } else {
+           lp->next = (krb5_kt_list)malloc(sizeof (*lp));
+           if (!lp->next) {
+               retval = ENOMEM;
+               break;
+           }
+           lp = lp->next;
+       }
+       lp->next = NULL;
+       lp->entry = entry;
+       if (!tail)
+           tail = lp;
+    }
+    if (entry) {
+       if (entry->magic == KV5M_KEYTAB_ENTRY)
+           krb5_kt_free_entry(context, entry);
+       krb5_xfree(entry);
+    }
+    if (retval) {
+       ktutil_free_kt_list(context, tail);
+       tail = NULL;
+       if (back)
+           back->next = NULL;
+    }
+    if (!*list)
+       *list = tail;
+    fclose(fp);
+    return retval;
+}
+
+/*
+ * Writes a kt_list out to a krb4 srvtab file.  Note that it first
+ * prunes the kt_list so that it won't contain any keys that are not
+ * the most recent, and ignores keys that are not KEYTYPE_DES.
+ */
+krb5_error_code ktutil_write_srvtab(context, list, name)
+    krb5_context context;
+    krb5_kt_list list;
+    char *name;
+{
+    krb5_kt_list lp, lp1, prev, pruned = NULL;
+    krb5_error_code retval = 0;
+    FILE *fp;
+    char sname[SNAME_SZ];
+    char sinst[INST_SZ];
+    char srealm[REALM_SZ];
+
+    /* First do heinous stuff to prune the list. */
+    for (lp = list; lp; lp = lp->next) {
+       if (lp->entry->key.keytype == KEYTYPE_DES) { /* only DES keys! */
+           for (lp1 = pruned; lp1; prev = lp1, lp1 = lp1->next) {
+               /* Hunt for the current principal in the pruned list */
+               if (krb5_principal_compare(context,
+                                          lp->entry->principal,
+                                          lp1->entry->principal))
+                   break;
+           }
+           if (!lp1) {         /* need to add entry to tail of pruned list */
+               if (!pruned) {
+                   pruned = (krb5_kt_list) malloc(sizeof (*pruned));
+                   if (!pruned)
+                       return ENOMEM;
+                   lp1 = pruned;
+               } else {
+                   prev->next
+                       = (krb5_kt_list) malloc(sizeof (*pruned));
+                   if (!prev->next) {
+                       retval = ENOMEM;
+                       goto free_pruned;
+                   }
+                   lp1 = prev->next;
+               }
+               lp1->entry = lp->entry;
+           } else if (lp1->entry->vno < lp->entry->vno)
+               /* Check if lp->entry is newer kvno; if so, update */
+               lp1->entry = lp->entry;
+       }
+    }
+    fp = fopen(name, "w");
+    if (!fp) {
+       retval = EIO;
+       goto free_pruned;
+    }
+    for (lp = pruned; lp; lp = lp->next) {
+       retval = krb5_524_conv_principal(context,
+                                        lp->entry->principal,
+                                        sname, sinst, srealm);
+       if (retval)
+           break;
+       fwrite(sname, strlen(sname) + 1, 1, fp);
+       fwrite(sinst, strlen(sinst) + 1, 1, fp);
+       fwrite(srealm, strlen(srealm) + 1, 1, fp);
+       fwrite((char *)&lp->entry->vno, 1, 1, fp);
+       fwrite((char *)lp->entry->key.contents,
+              sizeof (des_cblock), 1, fp);
+    }
+    fclose(fp);
+ free_pruned:
+    /*
+     * Loop over and free the pruned list; don't use free_kt_list
+     * because that kills the entries.
+     */
+    for (lp = pruned; lp;) {
+       prev = lp;
+       lp = lp->next;
+       krb5_xfree(prev);
+    }
+    return retval;
+}
+#endif /* KRB5_KRB4_COMPAT */