# # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Copyright (c) 2018, Joyent, Inc. PROG= ktutil OBJS = ktutil.o ktutil_ct.o ktutil_funcs.o SRCS = $(OBJS:.o=.c) CLOBBERFILES += $(TESTPROG) include ../../../Makefile.cmd include $(SRC)/lib/gss_mechs/mech_krb5/Makefile.mech_krb5 POFILE = $(PROG).po POFILES = generic.po CPPFLAGS += -I$(SRC)/uts/common/gssapi/include \ -I$(SRC)/lib/krb5 -I$(SRC)/lib/gss_mechs/mech_krb5/include \ -I$(SRC)/uts/common/gssapi/include \ -I$(SRC)/uts/common/gssapi/mechs/krb5/include \ -DHAVE_LIBSOCKET=1 -DHAVE_LIBNSL=1 -DHAVE_UNISTD_H=1 \ -DHAVE_SYS_TIMEB_H=1 -DHAVE_ALLOCA_H=1 -DHAVE_FTIME=1 \ -DHAVE_TIMEZONE CERRWARN += -Wno-implicit-function-declaration CERRWARN += -Wno-unused-function # not linted SMATCH=off LDFLAGS += $(KRUNPATH) $(KERBRUNPATH) LDLIBS += $(KMECHLIB) $(ROOT)$(KERBLIBDIR)/libss.so .KEEP_STATE: all: $(PROG) ktutil: $(OBJS) $(LINK.c) $(OBJS) -o $@ $(LDLIBS) $(POST_PROCESS) install: $(KRB5PROG) clean: $(RM) $(OBJS) include ../../../Makefile.targ $(POFILE): $(DERIVED_FILES) .WAIT $(POFILES) $(RM) $@ $(CAT) $(POFILES) > $@ $(RM) generic.po generic.po: $(RM) messages.po $(XGETTEXT) $(XGETFLAGS) `$(GREP) -l gettext *.[ch]` $(SED) "/^domain/d" messages.po > $@ $(RM) messages.po /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * * Openvision retains the copyright to derivative works of * this source code. Do *NOT* create a derivative of this * source code before consulting with your legal department. * Do *NOT* integrate *ANY* of this source code into another * product before consulting with your legal department. * * For further information, read the top-level Openvision * copyright which is contained in the top-level MIT Kerberos * copyright. * * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * */ /* * kadmin/ktutil/ktutil.c * * Copyright 1995, 1996 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. Furthermore if you modify this software you must label * your software as modified software and not distribute it in such a * fashion that it might be confused with the original M.I.T. software. * M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. * * SS user interface for ktutil. */ #include "k5-int.h" #include "ktutil.h" #include #include #include #ifdef HAVE_STDLIB_H #include #endif #include #include extern ss_request_table ktutil_cmds; krb5_context kcontext; krb5_kt_list ktlist = NULL; int main(argc, argv) int argc; char *argv[]; { krb5_error_code retval; int sci_idx; (void) setlocale(LC_ALL, ""); #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ #endif (void) textdomain(TEXT_DOMAIN); retval = krb5_init_context(&kcontext); if (retval) { com_err(argv[0], retval, gettext("while initializing krb5")); exit(1); } retval = ktutil_initialize_cmds_table (&ktutil_cmds); if (retval) { com_err(argv[0], retval, gettext("while localizing command description messages")); exit(1); } sci_idx = ss_create_invocation("ktutil", "5.0", (char *) NULL, &ktutil_cmds, &retval); if (retval) { ss_perror(sci_idx, retval, gettext("creating invocation")); exit(1); } retval = ss_listen(sci_idx); 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, gettext("%s: invalid arguments\n"), argv[0]); return; } retval = ktutil_free_kt_list(kcontext, ktlist); if (retval) com_err(argv[0], retval, gettext("while freeing ktlist")); ktlist = NULL; } void ktutil_read_v5(argc, argv) int argc; char *argv[]; { krb5_error_code retval; if (argc != 2) { fprintf(stderr, gettext("%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, gettext("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, gettext("%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, gettext("while reading srvtab \"%s\""), argv[1]); #else fprintf(stderr, gettext("%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, gettext("%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, gettext("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, gettext("%s: must specify srvtab to write\n"), argv[0]); return; } retval = ktutil_write_srvtab(kcontext, ktlist, argv[1]); if (retval) com_err(argv[0], retval, gettext("while writing srvtab \"%s\""), argv[1]); #else fprintf(stderr, gettext("%s: krb4 support not configured\n"), argv[0]); #endif } void ktutil_add_entry(argc, argv) int argc; char *argv[]; { krb5_error_code retval; char *princ = NULL; char *enctype = NULL; krb5_kvno kvno = 0; int use_pass = 0, use_key = 0, i; for (i = 1; i < argc; i++) { if ((strlen(argv[i]) == 2) && !strncmp(argv[i], "-p", 2)) { princ = argv[++i]; continue; } if ((strlen(argv[i]) == 2) && !strncmp(argv[i], "-k", 2)) { kvno = (krb5_kvno) atoi(argv[++i]); continue; } if ((strlen(argv[i]) == 2) && !strncmp(argv[i], "-e", 2)) { enctype = argv[++i]; continue; } if ((strlen(argv[i]) == 9) && !strncmp(argv[i], "-password", 9)) { use_pass++; continue; } if ((strlen(argv[i]) == 4) && !strncmp(argv[i], "-key", 4)) { use_key++; continue; } } if (argc != 8 || !(princ && kvno && enctype) || (use_pass+use_key != 1)) { fprintf(stderr, "%s: %s (-key | -password) -p principal " "-k kvno -e enctype\n", gettext("usage"), argv[0]); return; } retval = ktutil_add(kcontext, &ktlist, princ, kvno, enctype, use_pass); if (retval) com_err(argv[0], retval, gettext("while adding new entry")); } void ktutil_delete_entry(argc, argv) int argc; char *argv[]; { krb5_error_code retval; if (argc != 2) { fprintf(stderr, gettext("%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, gettext("while deleting entry %d"), atoi(argv[1])); } void ktutil_list(argc, argv) int argc; char *argv[]; { krb5_error_code retval; krb5_kt_list lp; int show_time = 0, show_keys = 0, show_enctype = 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; } if ((strlen(argv[i]) == 2) && !strncmp(argv[i], "-e", 2)) { show_enctype++; continue; } fprintf(stderr, "%s: %s [-t] [-k] [-e]\n", gettext("usage"), argv[0]); return; } if (show_time) { printf(gettext("slot KVNO Timestamp Principal\n")); printf("---- ---- ----------------- ---------------------------------------------------\n"); } else { printf(gettext("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, gettext("while unparsing principal name")); return; } printf("%4d %4d ", i, lp->entry->vno); if (show_time) { char fmtbuf[18]; char fill; time_t tstamp; (void) localtime(&tstamp); lp->entry->timestamp = tstamp; fill = ' '; if (!krb5_timestamp_to_sfstring((krb5_timestamp)lp->entry-> timestamp, fmtbuf, sizeof(fmtbuf), &fill)) printf("%s ", fmtbuf); } printf("%40s", pname); if (show_enctype) { static char buf[256]; if ((retval = krb5_enctype_to_string( lp->entry->key.enctype, buf, 256))) { if (retval == EINVAL) snprintf(buf, sizeof(buf), gettext("unsupported encryption type %d"), lp->entry->key.enctype); else { com_err(argv[0], retval, gettext("While converting " "enctype to string")); return; } } printf(" (%s) ", buf); } 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); } } /* * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * * Openvision retains the copyright to derivative works of * this source code. Do *NOT* create a derivative of this * source code before consulting with your legal department. * Do *NOT* integrate *ANY* of this source code into another * product before consulting with your legal department. * * For further information, read the top-level Openvision * copyright which is contained in the top-level MIT Kerberos * copyright. * * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * */ /* * 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. Furthermore if you modify this software you must label * your software as modified software and not distribute it in such a * fashion that it might be confused with the original M.I.T. software. * M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. * */ 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_context, krb5_kt_list); krb5_error_code ktutil_delete (krb5_context, krb5_kt_list *, int); krb5_error_code ktutil_add (krb5_context, krb5_kt_list *, char *, krb5_kvno, char *, int); krb5_error_code ktutil_read_keytab (krb5_context, char *, krb5_kt_list *); krb5_error_code ktutil_write_keytab (krb5_context, krb5_kt_list, char *); #ifdef KRB5_KRB4_COMPAT krb5_error_code ktutil_read_srvtab (krb5_context, char *, krb5_kt_list *); krb5_error_code ktutil_write_srvtab (krb5_context, krb5_kt_list, char *); #endif void ktutil_add_entry (int, char *[]); void ktutil_clear_list (int, char *[]); void ktutil_read_v5 (int, char *[]); void ktutil_read_v4 (int, char *[]); void ktutil_write_v5 (int, char *[]); void ktutil_write_v4 (int, char *[]); void ktutil_delete_entry (int, char *[]); void ktutil_list (int, char *[]); /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * * Openvision retains the copyright to derivative works of * this source code. Do *NOT* create a derivative of this * source code before consulting with your legal department. * Do *NOT* integrate *ANY* of this source code into another * product before consulting with your legal department. * * For further information, read the top-level Openvision * copyright which is contained in the top-level MIT Kerberos * copyright. * * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * */ /* ktutil_ct.c - automatically generated from ktutil_ct.ct */ /* Above no longer appears to be true */ #include #include #include "k5-int.h" /* * I18n hack. We sill define gettext(s) to be s here. That way the info_strings * will be extracted to the .po file. */ #define gettext(s) s #ifndef __STDC__ #define const #endif static char const * const ssu00001[] = { "clear_list", "clear", (char const *)0 }; extern void ktutil_clear_list __SS_PROTO; static char const * const ssu00002[] = { "read_kt", "rkt", (char const *)0 }; extern void ktutil_read_v5 __SS_PROTO; static char const * const ssu00003[] = { "read_st", "rst", (char const *)0 }; extern void ktutil_read_v4 __SS_PROTO; static char const * const ssu00004[] = { "write_kt", "wkt", (char const *)0 }; extern void ktutil_write_v5 __SS_PROTO; static char const * const ssu00005[] = { "write_st", "wst", (char const *)0 }; extern void ktutil_write_v4 __SS_PROTO; static char const * const ssu00006[] = { "add_entry", "addent", (char const *)0 }; extern void ktutil_add_entry __SS_PROTO; static char const * const ssu00007[] = { "delete_entry", "delent", (char const *)0 }; extern void ktutil_delete_entry __SS_PROTO; static char const * const ssu00008[] = { "list", "l", (char const *)0 }; extern void ktutil_list __SS_PROTO; static char const * const ssu00009[] = { "list_requests", "lr", "?", (char const *)0 }; extern void ss_list_requests __SS_PROTO; static char const * const ssu00010[] = { "quit", "exit", "q", (char const *)0 }; extern void ss_quit __SS_PROTO; static ss_request_entry ssu00011[] = { { ssu00001, ktutil_clear_list, gettext("Clear the current keylist."), 0 }, { ssu00002, ktutil_read_v5, gettext("Read a krb5 keytab into the current keylist."), 0 }, { ssu00003, ktutil_read_v4, gettext("Read a krb4 srvtab into the current keylist."), 0 }, { ssu00004, ktutil_write_v5, gettext("Write the current keylist to a krb5 keytab."), 0 }, { ssu00005, ktutil_write_v4, gettext("Write the current keylist to a krb4 srvtab."), 0 }, { ssu00006, ktutil_add_entry, gettext("Add an entry to the current keylist."), 0 }, { ssu00007, ktutil_delete_entry, gettext("Delete an entry from the current keylist."), 0 }, { ssu00008, ktutil_list, gettext("List the current keylist."), 0 }, { ssu00009, ss_list_requests, gettext("List available requests."), 0 }, { ssu00010, ss_quit, gettext("Exit program."), 0 }, { 0, 0, 0, 0 } }; ss_request_table ktutil_cmds = { 2, ssu00011 }; #undef gettext /* * This routine is responsible for localizing all the displayable * messages in the table. This was necessary since ktutil will be * invoking library calls that need to be able to display the messages * in the correct text domain (which only ktutil knows). * * This function assumes that the US version of the messages are * pre-loaded in the table and will be used should gettext not be * successful. This routine does NOT free the replaced strings as * its expected they may be in the heap (as above) and not malloc'ed. * If the caller malloc'ed the strings, they should retain pointers * and free them if not matching the contents of the table. */ krb5_error_code ktutil_initialize_cmds_table(ss_request_table *ktutil_cmds) { char *localized_text; ss_request_entry *ss_cmd; krb5_error_code retval = 0; if (ktutil_cmds) { for (ss_cmd = ktutil_cmds->requests; ss_cmd->info_string && *(ss_cmd->info_string) != '\0'; ++ss_cmd) { localized_text = gettext(ss_cmd->info_string); if ((strcmp(localized_text, ss_cmd->info_string)) != 0) { ss_cmd->info_string = strdup(localized_text); } } } else retval = EINVAL; return (retval); } /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * kadmin/ktutil/ktutil_funcs.c * *(C) Copyright 1995, 1996 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. Furthermore if you modify this software you must label * your software as modified software and not distribute it in such a * fashion that it might be confused with the original M.I.T. software. * M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. * * Utility functions for ktutil. */ #include "k5-int.h" #include "ktutil.h" #ifdef KRB5_KRB4_COMPAT #include "kerberosIV/krb.h" #include #endif #include #include #include /* * 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); free((char *)lp->entry); if (retval) break; prev = lp; lp = lp->next; free((char *)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, idx) krb5_context context; krb5_kt_list *list; int idx; { krb5_kt_list lp, prev; int i; for (lp = *list, i = 1; lp; prev = lp, lp = lp->next, i++) { if (i == idx) { if (i == 1) *list = lp->next; else prev->next = lp->next; lp->next = NULL; return ktutil_free_kt_list(context, lp); } } return EINVAL; } /* * Create a new keytab entry and add it to the keytab list. * Based on the value of use_pass, either prompt the user for a * password or key. If the keytab list is NULL, allocate a new * one first. */ krb5_error_code ktutil_add(context, list, princ_str, kvno, enctype_str, use_pass) krb5_context context; krb5_kt_list *list; char *princ_str; krb5_kvno kvno; char *enctype_str; int use_pass; { krb5_keytab_entry *entry; krb5_kt_list lp = NULL, prev = NULL; krb5_principal princ; krb5_enctype enctype; krb5_timestamp now; krb5_error_code retval; krb5_data password, salt; krb5_keyblock key; char buf[BUFSIZ]; char promptstr[1024]; char *cp; int i, tmp; unsigned int pwsize = BUFSIZ; retval = krb5_parse_name(context, princ_str, &princ); if (retval) return retval; /* now unparse in order to get the default realm appended to princ_str, if no realm was specified */ retval = krb5_unparse_name(context, princ, &princ_str); if (retval) return retval; retval = krb5_string_to_enctype(enctype_str, &enctype); if (retval) return KRB5_BAD_ENCTYPE; retval = krb5_timeofday(context, &now); if (retval) return retval; if (*list) { /* point lp at the tail of the list */ for (lp = *list; lp->next; lp = lp->next); } entry = (krb5_keytab_entry *) malloc(sizeof(krb5_keytab_entry)); if (!entry) { return ENOMEM; } memset((char *) entry, 0, sizeof(*entry)); if (!lp) { /* if list is empty, start one */ lp = (krb5_kt_list) malloc(sizeof(*lp)); if (!lp) { return ENOMEM; } } else { lp->next = (krb5_kt_list) malloc(sizeof(*lp)); if (!lp->next) { return ENOMEM; } prev = lp; lp = lp->next; } lp->next = NULL; lp->entry = entry; if (use_pass) { password.length = pwsize; password.data = (char *) malloc(pwsize); if (!password.data) { retval = ENOMEM; goto cleanup; } (void) snprintf(promptstr, sizeof(promptstr), gettext("Password for %.1000s"), princ_str); retval = krb5_read_password(context, promptstr, NULL, password.data, &password.length); if (retval) goto cleanup; retval = krb5_principal2salt(context, princ, &salt); if (retval) goto cleanup; retval = krb5_c_string_to_key(context, enctype, &password, &salt, &key); if (retval) goto cleanup; memset(password.data, 0, password.length); password.length = 0; memcpy(&lp->entry->key, &key, sizeof(krb5_keyblock)); } else { printf(gettext("Key for %s (hex): "), princ_str); fgets(buf, BUFSIZ, stdin); /* * We need to get rid of the trailing '\n' from fgets. * If we have an even number of hex digits (as we should), * write a '\0' over the '\n'. If for some reason we have * an odd number of hex digits, force an even number of hex * digits by writing a '0' into the last position (the string * will still be null-terminated). */ buf[strlen(buf) - 1] = strlen(buf) % 2 ? '\0' : '0'; if (strlen(buf) == 0) { fprintf(stderr, "addent: %s", gettext("Error reading key.\n")); retval = 0; goto cleanup; } lp->entry->key.enctype = enctype; lp->entry->key.contents = (krb5_octet *) malloc((strlen(buf) + 1) / 2); if (!lp->entry->key.contents) { retval = ENOMEM; goto cleanup; } i = 0; for (cp = buf; *cp; cp += 2) { if (!isxdigit((int) cp[0]) || !isxdigit((int) cp[1])) { fprintf(stderr, "addent: %s", gettext("Illegal character in key.\n")); retval = 0; goto cleanup; } sscanf(cp, "%02x", &tmp); lp->entry->key.contents[i++] = (krb5_octet) tmp; } lp->entry->key.length = i; } lp->entry->principal = princ; lp->entry->vno = kvno; lp->entry->timestamp = now; if (!*list) *list = lp; return 0; cleanup: if (prev) prev->next = NULL; ktutil_free_kt_list(context, lp); return retval; } /* * 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) free((char *)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:"); if (strlen (name) >= MAXPATHLEN) return ENAMETOOLONG; 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 /* * getstr() 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. getstr() returns the number of characters read, * including the null terminator. */ static int getstr(fp, s, n) FILE *fp; register char *s; int n; { register int 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 (!(getstr(fp, sname, SNAME_SZ) > 0 && getstr(fp, sinst, INST_SZ) > 0 && getstr(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.enctype = ENCTYPE_DES_CBC_CRC; 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); free((char *)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 ENCTYPE_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.enctype != ENCTYPE_DES_CBC_CRC) && (lp->entry->key.enctype != ENCTYPE_DES_CBC_MD5) && (lp->entry->key.enctype != ENCTYPE_DES_CBC_MD4) && (lp->entry->key.enctype != ENCTYPE_DES_CBC_RAW)) continue; 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; memset((char *) pruned, 0, sizeof(*pruned)); lp1 = pruned; } else { prev->next = (krb5_kt_list) malloc(sizeof (*pruned)); if (!prev->next) { retval = ENOMEM; goto free_pruned; } memset((char *) prev->next, 0, sizeof(*pruned)); lp1 = prev->next; } lp1->entry = lp->entry; } else { /* This heuristic should be roughly the same as in the keytab-reading code in libkrb5. */ int offset = 0; if (lp1->entry->vno > 240 || lp->entry->vno > 240) { offset = 128; } #define M(X) (((X) + offset) % 256) if (M(lp1->entry->vno) < M(lp->entry->vno)) /* Check if lp->entry is newer kvno; if so, update */ lp1->entry = lp->entry; } } umask(0077); /*Changing umask for all of ktutil is OK * We don't ever write out anything that should use * default umask.*/ fp = fopen(name, "w"); if (!fp) { retval = EIO; goto free_pruned; } for (lp = pruned; lp; lp = lp->next) { unsigned char kvno; kvno = (unsigned char) lp->entry->vno; 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 *)&kvno, 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; free((char *)prev); } return retval; } #endif /* KRB5_KRB4_COMPAT */