# # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # cmd/krb5/Makefile # # Build everything in parallel; use .WAIT for dependencies .PARALLEL: # Hammerhead: ldap_util (kdb5_ldap_util) removed (libsldap chain removal, # Phase 1). SUBDIRS= \ kwarn .WAIT \ krb5kdc \ kadmin \ kdestroy \ kinit \ klist \ kproplog \ krb5-config \ slave all : TARGET= all clean : TARGET= clean clobber : TARGET= clobber delete : TARGET= delete install : TARGET= install catalog : TARGET= catalog package : TARGET= package _msg : TARGET= _msg .KEEP_STATE: all clean clobber delete install catalog package: $(SUBDIRS) _msg: $(SUBDIRS) check: $(CHECKHDRS) $(filter-out .WAIT,$(SUBDIRS)): FRC @cd $@; pwd; $(MAKE) $(TARGET) FRC: /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifdef RPC_XDR %#include "iprop.h" #endif /* RPC_XDR */ /* * Initial declarations */ #ifndef RPC_HDR typedef short int16_t; typedef unsigned short uint16_t; typedef int int32_t; typedef unsigned int uint32_t; typedef hyper int64_t; typedef unsigned hyper uint64_t; #endif /* !RPC_HDR */ typedef opaque utf8str_t<>; /* * Transaction log serial no. */ typedef uint32_t kdb_sno_t; /* Timestamp */ struct kdbe_time_t { uint32_t seconds; uint32_t useconds; }; /* Key Data */ struct kdbe_key_t { int32_t k_ver; /* Version */ int32_t k_kvno; /* Key version no. */ int32_t k_enctype<>; utf8str_t k_contents<>; }; /* Content data */ struct kdbe_data_t { int32_t k_magic; utf8str_t k_data; }; /* Principal Data */ struct kdbe_princ_t { utf8str_t k_realm; kdbe_data_t k_components<>; int32_t k_nametype; }; /* TL data (pre-auth specific data) */ struct kdbe_tl_t { int16_t tl_type; opaque tl_data<>; }; /* Structure to store pwd history */ typedef kdbe_key_t kdbe_pw_hist_t<>; /* Basic KDB entry attributes */ enum kdbe_attr_type_t { AT_ATTRFLAGS = 0, AT_MAX_LIFE = 1, AT_MAX_RENEW_LIFE = 2, AT_EXP = 3, AT_PW_EXP = 4, AT_LAST_SUCCESS = 5, AT_LAST_FAILED = 6, AT_FAIL_AUTH_COUNT = 7, AT_PRINC = 8, AT_KEYDATA = 9, AT_TL_DATA = 10, AT_LEN = 11, AT_MOD_PRINC = 12, AT_MOD_TIME = 13, AT_MOD_WHERE = 14, AT_PW_LAST_CHANGE = 15, AT_PW_POLICY = 16, AT_PW_POLICY_SWITCH = 17, AT_PW_HIST_KVNO = 18, AT_PW_HIST = 19 }; /* KDB entry, Attribute=value */ union kdbe_val_t switch (kdbe_attr_type_t av_type) { case AT_ATTRFLAGS: uint32_t av_attrflags; case AT_MAX_LIFE: uint32_t av_max_life; case AT_MAX_RENEW_LIFE: uint32_t av_max_renew_life; case AT_EXP: uint32_t av_exp; case AT_PW_EXP: uint32_t av_pw_exp; case AT_LAST_SUCCESS: uint32_t av_last_success; case AT_LAST_FAILED: uint32_t av_last_failed; case AT_FAIL_AUTH_COUNT: uint32_t av_fail_auth_count; case AT_PRINC: kdbe_princ_t av_princ; case AT_KEYDATA: kdbe_key_t av_keydata<>; /* array of keys */ case AT_TL_DATA: kdbe_tl_t av_tldata<>; /* array of TL data */ case AT_LEN: int16_t av_len; case AT_PW_LAST_CHANGE: uint32_t av_pw_last_change; case AT_MOD_PRINC: kdbe_princ_t av_mod_princ; case AT_MOD_TIME: uint32_t av_mod_time; case AT_MOD_WHERE: utf8str_t av_mod_where; case AT_PW_POLICY: utf8str_t av_pw_policy; case AT_PW_POLICY_SWITCH: bool av_pw_policy_switch; case AT_PW_HIST_KVNO: uint32_t av_pw_hist_kvno; case AT_PW_HIST: kdbe_pw_hist_t av_pw_hist<>; /* array of pw history */ default: opaque av_extension<>; /* futures */ }; typedef kdbe_val_t kdbe_t<>; /* Array of attr/val makes a KDB entry */ /* * Incremental update */ struct kdb_incr_update_t { utf8str_t kdb_princ_name; /* Principal name */ kdb_sno_t kdb_entry_sno; /* Serial # of entry */ kdbe_time_t kdb_time; /* Timestamp of update */ kdbe_t kdb_update; /* Attributes modified */ bool kdb_deleted; /* Is this update a DELETION ? */ bool kdb_commit; /* Is the entry committed or not ? */ utf8str_t kdb_kdcs_seen_by<>; /* Names of slaves that have */ /* seen this update - for */ /* future use */ opaque kdb_futures<>; /* futures */ }; /* * Update log body */ typedef kdb_incr_update_t kdb_ulog_t<>; enum update_status_t { UPDATE_OK = 0, UPDATE_ERROR = 1, UPDATE_FULL_RESYNC_NEEDED = 2, UPDATE_BUSY = 3, UPDATE_NIL = 4, UPDATE_PERM_DENIED = 5 }; struct kdb_last_t { kdb_sno_t last_sno; kdbe_time_t last_time; }; struct kdb_incr_result_t { kdb_last_t lastentry; kdb_ulog_t updates; update_status_t ret; }; struct kdb_fullresync_result_t { kdb_last_t lastentry; update_status_t ret; }; program KRB5_IPROP_PROG { version KRB5_IPROP_VERS { /* * NULL procedure */ void IPROP_NULL(void) = 0; /* * Keep waiting for and get next incremental update(s) * * Will return latest kdb_vers on the master (if different), * alongwith return value and affected db entries. */ kdb_incr_result_t IPROP_GET_UPDATES(kdb_last_t) = 1; /* * We need to do the full-resync of the db, since the * serial nos./timestamps are way out-of-whack */ kdb_fullresync_result_t IPROP_FULL_RESYNC(void) = 2; } = 1; } = 100423; /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _IPROP_HDR_H #define _IPROP_HDR_H #ifdef __cplusplus extern "C" { #endif /* * This file has some defines common to the iprop client and * server routines. */ /* * Maximum size for each ulog entry is 2KB and maximum * possible attribute-value pairs for each ulog entry is 20 */ #define MAXENTRY_SIZE 2048 #define MAXATTRS_SIZE 20 #define KIPROP_SVC_NAME "kiprop" #define MAX_BACKOFF 300 /* Backoff for a maximum for 5 mts */ enum iprop_role { IPROP_NULL = 0, IPROP_MASTER = 1, IPROP_SLAVE = 2 }; typedef enum iprop_role iprop_role; #ifdef __cplusplus } #endif #endif /* !_IPROP_HDR_H */ # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # cmd/krb5/kadmin/Makefile include ../../Makefile.cmd SUBDIRS= cli dbutil ktutil kpasswd server kclient kdcmgr all : TARGET= all clean : TARGET= clean clobber : TARGET= clobber delete : TARGET= delete install : TARGET= install catalog : TARGET= catalog package : TARGET= package _msg: TARGET= _msg _msg: $(SUBDIRS) .KEEP_STATE: all clean clobber delete install catalog package: $(SUBDIRS) # install rule for install_h target install: $(SUBDIRS) check: $(CHECKHDRS) $(SUBDIRS): FRC @cd $@; pwd; $(MAKE) $(TARGET) FRC: # # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Copyright (c) 2018, Joyent, Inc. PROG= kadmin kadmin.local SHFILES= k5srvutil CLOBBERFILES= $(SHFILES) KRB5SBINSHFILES= $(SHFILES:%=$(KRB5SBIN)/%) COMMON_OBJS = kadmin.o kadmin_ct.o ss_wrapper.o getdate.o keytab.o RMT_OBJS= $(COMMON_OBJS) kadmin_rmt.o # Hammerhead: keytab_loc.o compiled with -D_KADMIN_LOCAL_ to exclude # kadm5_randkey_principal_old (only in libkadm5clnt, not libkadm5srv) LOC_OBJS= kadmin.o kadmin_ct.o ss_wrapper.o getdate.o keytab_loc.o kadmin_loc.o OBJS = $(COMMON_OBJS) $(RMT_OBJS) $(LOC_OBJS) include ../../../Makefile.cmd include $(SRC)/lib/gss_mechs/mech_krb5/Makefile.mech_krb5 POFILE = generic.po POFILES = kadmin.po k5srvutil.po DEFS = -DHAVE_LIBSOCKET=1 -DHAVE_LIBNSL=1 -DHAVE_UNISTD_H=1 -DHAVE_SYS_TIMEB_H=1 \ -DHAVE_ALLOCA_H=1 -DHAVE_FTIME=1 -DHAVE_TIMEZONE CPPFLAGS += -I$(SRC)/uts/common/gssapi/include/ \ -I$(SRC)/uts/common/gssapi/mechs/krb5/include \ -I$(SRC)/uts/common/gssapi/include \ -I$(SRC)/cmd/krb5/iprop \ -I$(SRC)/lib/krb5 \ -I$(SRC)/lib/gss_mechs/mech_krb5/include $(DEFS) CERRWARN += -Wno-implicit-function-declaration CERRWARN += -Wno-unused-function CERRWARN += -Wno-parentheses CERRWARN += -Wno-unused-variable # because of labels from yacc getdate.o : CERRWARN += -Wno-unused-label SMATCH=off SHAREDLIBS = -lmech_krb5 -lss # Hammerhead: GNU ld needs rpath-link for transitive deps (libdyn, libkdb) via libkadm5srv LDFLAGS += -Wl,-rpath-link,$(ROOT)/usr/lib/krb5 CLLIBS = $(LDLIBS) $(KRUNPATH) $(KERBRUNPATH) \ -L$(ROOT_KLIBDIR) -L$(KRB5LIB) $(SHAREDLIBS) -lkadm5clnt SRVLIBS = $(LDLIBS) $(KRUNPATH) $(KERBRUNPATH) \ -L$(ROOT_KLIBDIR) -L$(KRB5LIB) $(SHAREDLIBS) -lkadm5srv kadmin.local: DEFS += -D_KADMIN_LOCAL_ .KEEP_STATE: all: $(PROG) $(SHFILES) kadmin: $(RMT_OBJS) $(LINK.c) $(RMT_OBJS) -o $@ $(CLLIBS) $(POST_PROCESS) keytab_loc.o: keytab.c $(COMPILE.c) -D_KADMIN_LOCAL_ -o $@ $< $(POST_PROCESS_O) kadmin.local: $(LOC_OBJS) $(LINK.c) $(LOC_OBJS) -o $@ $(SRVLIBS) $(POST_PROCESS) $(SHFILES): $(SHFILES).sh $(RM) $(SHFILES) $(CP) $(SHFILES).sh $(SHFILES) install: $(KRB5SBINPROG) $(KRB5SBINSHFILES) clean: $(RM) $(OBJS) include ../../../Makefile.targ $(POFILE): $(DERIVED_FILES) .WAIT $(POFILES) $(RM) $@ $(CAT) $(POFILES) > $@ kadmin.po: FRC $(RM) messages.po $(XGETTEXT) $(XGETFLAGS) `$(GREP) -l gettext *.[ch]` $(SED) "/^domain/d" messages.po > $@ $(RM) messages.po FRC: /* * 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 * */ %{ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* ** Originally written by Steven M. Bellovin while ** at the University of North Carolina at Chapel Hill. Later tweaked by ** a couple of people on Usenet. Completely overhauled by Rich $alz ** and Jim Berets in August, 1990; ** send any email to Rich. ** ** This grammar has nine shift/reduce conflicts. ** ** This code is in the public domain and has no copyright. */ /* SUPPRESS 287 on yaccpar_sccsid *//* Unusd static variable */ /* SUPPRESS 288 on yyerrlab *//* Label unused */ #ifdef HAVE_CONFIG_H #if defined (emacs) || defined (CONFIG_BROKETS) #include #else #include "config.h" #endif #endif #include /* Since the code of getdate.y is not included in the Emacs executable itself, there is no need to #define static in this file. Even if the code were included in the Emacs executable, it probably wouldn't do any harm to #undef it here; this will only cause problems if we try to write to a static variable, which I don't think this code needs to do. */ #ifdef emacs #undef static #endif /* The following block of alloca-related preprocessor directives is here solely to allow compilation by non GNU-C compilers of the C parser produced from this file by old versions of bison. Newer versions of bison include a block similar to this one in bison.simple. */ #ifdef __GNUC__ #undef alloca #define alloca __builtin_alloca #else #ifdef HAVE_ALLOCA_H #include #else #ifdef _AIX /* for Bison */ #pragma alloca #else void *alloca (); #endif #endif #endif #include #include #if defined(HAVE_STDLIB_H) #include #endif /* The code at the top of get_date which figures out the offset of the current time zone checks various CPP symbols to see if special tricks are need, but defaults to using the gettimeofday system call. Include if that will be used. */ #if defined(vms) #include #include #else #include #ifdef TIME_WITH_SYS_TIME #include #include #else #ifdef HAVE_SYS_TIME_H #include #else #include #endif #endif #ifdef timezone #undef timezone /* needed for sgi */ #endif /* ** We use the obsolete `struct my_timeb' as part of our interface! ** Since the system doesn't have it, we define it here; ** our callers must do likewise. */ struct my_timeb { time_t time; /* Seconds since the epoch */ unsigned short millitm; /* Field not used */ short timezone; /* Minutes west of GMT */ short dstflag; /* Field not used */ }; #endif /* defined(vms) */ #if defined (STDC_HEADERS) || defined (USG) #include #endif /* Some old versions of bison generate parsers that use bcopy. That loses on systems that don't provide the function, so we have to redefine it here. */ #ifndef bcopy #define bcopy(from, to, len) memcpy ((to), (from), (len)) #endif /* * The following is a hack so that it is easy to internationalize * statically declared strings. We define a wrapper function here that * will be a replacement for gettext. We the make gettext a macro that * just returns its argument, which now can be used with statically defined * strings. The conquence of this is that GETTEXT must be used to translate * a string at runtime and gettext must be used around string literals so * that xgettext command can extract them to a portable object database file. * * Thus to translate a string literal that is an argument to a function foo * the following will have to be performed: * * foo(GETTEXT(gettext("This is a test"))); * * The inner gettext call is for xgettext command to extract the string. * The C preprossesor will reduce the above to: * * foo(GETTEXT(("This ia a test")); */ #include static char * GETTEXT(const char *msgid) { return (gettext(msgid)); } #define gettext(s) (s) extern struct tm *gmtime(); extern struct tm *localtime(); #define yyparse getdate_yyparse #define yylex getdate_yylex #define yyerror getdate_yyerror static int getdate_yylex (void); static int getdate_yyerror (char *); #define EPOCH 1970 #define EPOCH_END 2099 /* Solaris 64 bit can support this at this point */ #define HOUR(x) ((time_t)(x) * 60) #define SECSPERDAY (24L * 60L * 60L) /* ** An entry in the lexical lookup table. */ typedef struct _TABLE { char *name; int type; time_t value; } TABLE; /* ** Daylight-savings mode: on, off, or not yet known. */ typedef enum _DSTMODE { DSTon, DSToff, DSTmaybe } DSTMODE; /* ** Meridian: am, pm, or 24-hour style. */ typedef enum _MERIDIAN { MERam, MERpm, MER24 } MERIDIAN; /* ** Global variables. We could get rid of most of these by using a good ** union as the yacc stack. (This routine was originally written before ** yacc had the %union construct.) Maybe someday; right now we only use ** the %union very rarely. */ static char *yyInput; static DSTMODE yyDSTmode; static time_t yyDayOrdinal; static time_t yyDayNumber; static int yyHaveDate; static int yyHaveDay; static int yyHaveRel; static int yyHaveTime; static int yyHaveZone; static time_t yyTimezone; static time_t yyDay; static time_t yyHour; static time_t yyMinutes; static time_t yyMonth; static time_t yySeconds; static time_t yyYear; static MERIDIAN yyMeridian; static time_t yyRelMonth; static time_t yyRelSeconds; %} %union { time_t Number; enum _MERIDIAN Meridian; } %token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT %token tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST tNEVER %type tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT %type tSEC_UNIT tSNUMBER tUNUMBER tZONE %type tMERIDIAN o_merid %% spec : /* NULL */ | spec item | tNEVER { yyYear = 1970; yyMonth = 1; yyDay = 1; yyHour = yyMinutes = yySeconds = 0; yyDSTmode = DSToff; yyTimezone = 0; /* gmt */ yyHaveDate++; } ; item : time { yyHaveTime++; } | zone { yyHaveZone++; } | date { yyHaveDate++; } | day { yyHaveDay++; } | rel { yyHaveRel++; } ; time : tUNUMBER tMERIDIAN { yyHour = $1; yyMinutes = 0; yySeconds = 0; yyMeridian = $2; } | tUNUMBER ':' tUNUMBER o_merid { yyHour = $1; yyMinutes = $3; yySeconds = 0; yyMeridian = $4; } | tUNUMBER ':' tUNUMBER tSNUMBER { yyHour = $1; yyMinutes = $3; yyMeridian = MER24; yyDSTmode = DSToff; yyTimezone = - ($4 % 100 + ($4 / 100) * 60); } | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid { yyHour = $1; yyMinutes = $3; yySeconds = $5; yyMeridian = $6; } | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER { yyHour = $1; yyMinutes = $3; yySeconds = $5; yyMeridian = MER24; yyDSTmode = DSToff; yyTimezone = - ($6 % 100 + ($6 / 100) * 60); } ; zone : tZONE { yyTimezone = $1; yyDSTmode = DSToff; } | tDAYZONE { yyTimezone = $1; yyDSTmode = DSTon; } | tZONE tDST { yyTimezone = $1; yyDSTmode = DSTon; } ; day : tDAY { yyDayOrdinal = 1; yyDayNumber = $1; } | tDAY ',' { yyDayOrdinal = 1; yyDayNumber = $1; } | tUNUMBER tDAY { yyDayOrdinal = $1; yyDayNumber = $2; } ; date : tUNUMBER '/' tUNUMBER { yyMonth = $1; yyDay = $3; } | tUNUMBER '/' tUNUMBER '/' tUNUMBER { yyMonth = $1; yyDay = $3; yyYear = $5; } | tUNUMBER tSNUMBER tSNUMBER { /* ISO 8601 format. yyyy-mm-dd. */ yyYear = $1; yyMonth = -$2; yyDay = -$3; } | tUNUMBER tMONTH tSNUMBER { /* e.g. 17-JUN-1992. */ yyDay = $1; yyMonth = $2; yyYear = -$3; } | tMONTH tUNUMBER { yyMonth = $1; yyDay = $2; } | tMONTH tUNUMBER ',' tUNUMBER { yyMonth = $1; yyDay = $2; yyYear = $4; } | tUNUMBER tMONTH { yyMonth = $2; yyDay = $1; } | tUNUMBER tMONTH tUNUMBER { yyMonth = $2; yyDay = $1; yyYear = $3; } ; rel : relunit tAGO { yyRelSeconds = -yyRelSeconds; yyRelMonth = -yyRelMonth; } | relunit ; relunit : tUNUMBER tMINUTE_UNIT { yyRelSeconds += $1 * $2 * 60L; } | tSNUMBER tMINUTE_UNIT { yyRelSeconds += $1 * $2 * 60L; } | tMINUTE_UNIT { yyRelSeconds += $1 * 60L; } | tSNUMBER tSEC_UNIT { yyRelSeconds += $1; } | tUNUMBER tSEC_UNIT { yyRelSeconds += $1; } | tSEC_UNIT { yyRelSeconds++; } | tSNUMBER tMONTH_UNIT { yyRelMonth += $1 * $2; } | tUNUMBER tMONTH_UNIT { yyRelMonth += $1 * $2; } | tMONTH_UNIT { yyRelMonth += $1; } ; o_merid : /* NULL */ { $$ = MER24; } | tMERIDIAN { $$ = $1; } ; %% /* Month and day table. */ static TABLE const MonthDayTable[] = { { gettext("january"), tMONTH, 1 }, { gettext("february"), tMONTH, 2 }, { gettext("march"), tMONTH, 3 }, { gettext("april"), tMONTH, 4 }, { gettext("may"), tMONTH, 5 }, { gettext("june"), tMONTH, 6 }, { gettext("july"), tMONTH, 7 }, { gettext("august"), tMONTH, 8 }, { gettext("september"), tMONTH, 9 }, { gettext("sept"), tMONTH, 9 }, { gettext("october"), tMONTH, 10 }, { gettext("november"), tMONTH, 11 }, { gettext("december"), tMONTH, 12 }, { gettext("sunday"), tDAY, 0 }, { gettext("monday"), tDAY, 1 }, { gettext("tuesday"), tDAY, 2 }, { gettext("tues"), tDAY, 2 }, { gettext("wednesday"), tDAY, 3 }, { gettext("wednes"), tDAY, 3 }, { gettext("thursday"), tDAY, 4 }, { gettext("thur"), tDAY, 4 }, { gettext("thurs"), tDAY, 4 }, { gettext("friday"), tDAY, 5 }, { gettext("saturday"), tDAY, 6 }, { NULL } }; /* Time units table. */ static TABLE const UnitsTable[] = { { gettext("year"), tMONTH_UNIT, 12 }, { gettext("month"), tMONTH_UNIT, 1 }, { gettext("fortnight"), tMINUTE_UNIT, 14 * 24 * 60 }, { gettext("week"), tMINUTE_UNIT, 7 * 24 * 60 }, { gettext("day"), tMINUTE_UNIT, 1 * 24 * 60 }, { gettext("hour"), tMINUTE_UNIT, 60 }, { gettext("minute"), tMINUTE_UNIT, 1 }, { gettext("min"), tMINUTE_UNIT, 1 }, { gettext("second"), tSEC_UNIT, 1 }, { gettext("sec"), tSEC_UNIT, 1 }, { NULL } }; /* Assorted relative-time words. */ static TABLE const OtherTable[] = { { gettext("tomorrow"), tMINUTE_UNIT, 1 * 24 * 60 }, { gettext("yesterday"), tMINUTE_UNIT, -1 * 24 * 60 }, { gettext("today"), tMINUTE_UNIT, 0 }, { gettext("now"), tMINUTE_UNIT, 0 }, { gettext("last"), tUNUMBER, -1 }, { gettext("this"), tMINUTE_UNIT, 0 }, { gettext("next"), tUNUMBER, 2 }, { gettext("first"), tUNUMBER, 1 }, /* { gettext("second"), tUNUMBER, 2 }, */ { gettext("third"), tUNUMBER, 3 }, { gettext("fourth"), tUNUMBER, 4 }, { gettext("fifth"), tUNUMBER, 5 }, { gettext("sixth"), tUNUMBER, 6 }, { gettext("seventh"), tUNUMBER, 7 }, { gettext("eighth"), tUNUMBER, 8 }, { gettext("ninth"), tUNUMBER, 9 }, { gettext("tenth"), tUNUMBER, 10 }, { gettext("eleventh"), tUNUMBER, 11 }, { gettext("twelfth"), tUNUMBER, 12 }, { gettext("ago"), tAGO, 1 }, { gettext("never"), tNEVER, 0 }, { NULL } }; /* The timezone table. */ /* Some of these are commented out because a time_t can't store a float. */ static TABLE const TimezoneTable[] = { { gettext("gmt"), tZONE, HOUR( 0) }, /* Greenwich Mean */ { gettext("ut"), tZONE, HOUR( 0) }, /* Universal (Coordinated) */ { gettext("utc"), tZONE, HOUR( 0) }, { gettext("wet"), tZONE, HOUR( 0) }, /* Western European */ { gettext("bst"), tDAYZONE, HOUR( 0) }, /* British Summer */ { gettext("wat"), tZONE, HOUR( 1) }, /* West Africa */ { gettext("at"), tZONE, HOUR( 2) }, /* Azores */ #if 0 /* For completeness. BST is also British Summer, and GST is * also Guam Standard. */ { gettext("bst"), tZONE, HOUR( 3) }, /* Brazil Standard */ { gettext("gst"), tZONE, HOUR( 3) }, /* Greenland Standard */ #endif #if 0 { gettext("nft"), tZONE, HOUR(3.5) }, /* Newfoundland */ { gettext("nst"), tZONE, HOUR(3.5) }, /* Newfoundland Standard */ { gettext("ndt"), tDAYZONE, HOUR(3.5) }, /* Newfoundland Daylight */ #endif { gettext("ast"), tZONE, HOUR( 4) }, /* Atlantic Standard */ { gettext("adt"), tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */ { gettext("est"), tZONE, HOUR( 5) }, /* Eastern Standard */ { gettext("edt"), tDAYZONE, HOUR( 5) }, /* Eastern Daylight */ { gettext("cst"), tZONE, HOUR( 6) }, /* Central Standard */ { gettext("cdt"), tDAYZONE, HOUR( 6) }, /* Central Daylight */ { gettext("mst"), tZONE, HOUR( 7) }, /* Mountain Standard */ { gettext("mdt"), tDAYZONE, HOUR( 7) }, /* Mountain Daylight */ { gettext("pst"), tZONE, HOUR( 8) }, /* Pacific Standard */ { gettext("pdt"), tDAYZONE, HOUR( 8) }, /* Pacific Daylight */ { gettext("yst"), tZONE, HOUR( 9) }, /* Yukon Standard */ { gettext("ydt"), tDAYZONE, HOUR( 9) }, /* Yukon Daylight */ { gettext("hst"), tZONE, HOUR(10) }, /* Hawaii Standard */ { gettext("hdt"), tDAYZONE, HOUR(10) }, /* Hawaii Daylight */ { gettext("cat"), tZONE, HOUR(10) }, /* Central Alaska */ { gettext("ahst"), tZONE, HOUR(10) }, /* Alaska-Hawaii Standard */ { gettext("nt"), tZONE, HOUR(11) }, /* Nome */ { gettext("idlw"), tZONE, HOUR(12) }, /* International Date Line West */ { gettext("cet"), tZONE, -HOUR(1) }, /* Central European */ { gettext("met"), tZONE, -HOUR(1) }, /* Middle European */ { gettext("mewt"), tZONE, -HOUR(1) }, /* Middle European Winter */ { gettext("mest"), tDAYZONE, -HOUR(1) }, /* Middle European Summer */ { gettext("swt"), tZONE, -HOUR(1) }, /* Swedish Winter */ { gettext("sst"), tDAYZONE, -HOUR(1) }, /* Swedish Summer */ { gettext("fwt"), tZONE, -HOUR(1) }, /* French Winter */ { gettext("fst"), tDAYZONE, -HOUR(1) }, /* French Summer */ { gettext("eet"), tZONE, -HOUR(2) }, /* Eastern Europe, USSR Zone 1 */ { gettext("bt"), tZONE, -HOUR(3) }, /* Baghdad, USSR Zone 2 */ #if 0 { gettext("it"), tZONE, -HOUR(3.5) },/* Iran */ #endif { gettext("zp4"), tZONE, -HOUR(4) }, /* USSR Zone 3 */ { gettext("zp5"), tZONE, -HOUR(5) }, /* USSR Zone 4 */ #if 0 { gettext("ist"), tZONE, -HOUR(5.5) },/* Indian Standard */ #endif { gettext("zp6"), tZONE, -HOUR(6) }, /* USSR Zone 5 */ #if 0 /* For completeness. NST is also Newfoundland Stanard, and SST is * also Swedish Summer. */ { gettext("nst"), tZONE, -HOUR(6.5) },/* North Sumatra */ { gettext("sst"), tZONE, -HOUR(7) }, /* South Sumatra, USSR Zone 6 */ #endif /* 0 */ { gettext("wast"), tZONE, -HOUR(7) }, /* West Australian Standard */ { gettext("wadt"), tDAYZONE, -HOUR(7) }, /* West Australian Daylight */ #if 0 { gettext("jt"), tZONE, -HOUR(7.5) },/* Java (3pm in Cronusland!) */ #endif { gettext("cct"), tZONE, -HOUR(8) }, /* China Coast, USSR Zone 7 */ { gettext("jst"), tZONE, -HOUR(9) }, /* Japan Standard, USSR Zone 8 */ { gettext("kst"), tZONE, -HOUR(9) }, /* Korean Standard */ #if 0 { gettext("cast"), tZONE, -HOUR(9.5) },/* Central Australian Standard */ { gettext("cadt"), tDAYZONE, -HOUR(9.5) },/* Central Australian Daylight */ #endif { gettext("east"), tZONE, -HOUR(10) }, /* Eastern Australian Standard */ { gettext("eadt"), tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */ { gettext("gst"), tZONE, -HOUR(10) }, /* Guam Standard, USSR Zone 9 */ { gettext("kdt"), tZONE, -HOUR(10) }, /* Korean Daylight */ { gettext("nzt"), tZONE, -HOUR(12) }, /* New Zealand */ { gettext("nzst"), tZONE, -HOUR(12) }, /* New Zealand Standard */ { gettext("nzdt"), tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */ { gettext("idle"), tZONE, -HOUR(12) }, /* International Date Line East */ { NULL } }; /* ARGSUSED */ static int yyerror(s) char *s; { return 0; } static time_t ToSeconds(Hours, Minutes, Seconds, Meridian) time_t Hours; time_t Minutes; time_t Seconds; MERIDIAN Meridian; { if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59) return -1; switch (Meridian) { case MER24: if (Hours < 0 || Hours > 23) return -1; return (Hours * 60L + Minutes) * 60L + Seconds; case MERam: if (Hours < 1 || Hours > 12) return -1; return (Hours * 60L + Minutes) * 60L + Seconds; case MERpm: if (Hours < 1 || Hours > 12) return -1; return ((Hours + 12) * 60L + Minutes) * 60L + Seconds; default: abort (); } /* NOTREACHED */ } /* * From hh:mm:ss [am|pm] mm/dd/yy [tz], compute and return the number * of seconds since 00:00:00 1/1/70 GMT. */ static time_t Convert(Month, Day, Year, Hours, Minutes, Seconds, Meridian, DSTmode) time_t Month; time_t Day; time_t Year; time_t Hours; time_t Minutes; time_t Seconds; MERIDIAN Meridian; DSTMODE DSTmode; { static int DaysInMonth[12] = { 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; time_t tod; time_t Julian; int i; if (Year < 0) Year = -Year; if (Year < 1900) Year += 1900; DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0) ? 29 : 28; if (Year < EPOCH || Year > EPOCH_END || Month < 1 || Month > 12 /* Lint fluff: "conversion from long may lose accuracy" */ || Day < 1 || Day > DaysInMonth[(int)--Month]) return -1; for (Julian = Day - 1, i = 0; i < Month; i++) Julian += DaysInMonth[i]; for (i = EPOCH; i < Year; i++) Julian += 365 + ((i % 4 == 0) && ((Year % 100 != 0) || (Year % 400 == 0))); Julian *= SECSPERDAY; Julian += yyTimezone * 60L; if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0) return -1; Julian += tod; if (DSTmode == DSTon || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst)) Julian -= 60 * 60; return Julian; } static time_t DSTcorrect(Start, Future) time_t Start; time_t Future; { time_t StartDay; time_t FutureDay; StartDay = (localtime(&Start)->tm_hour + 1) % 24; FutureDay = (localtime(&Future)->tm_hour + 1) % 24; return (Future - Start) + (StartDay - FutureDay) * 60L * 60L; } static time_t RelativeDate(Start, DayOrdinal, DayNumber) time_t Start; time_t DayOrdinal; time_t DayNumber; { struct tm *tm; time_t now; now = Start; tm = localtime(&now); now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7); now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1); return DSTcorrect(Start, now); } static time_t RelativeMonth(Start, RelMonth) time_t Start; time_t RelMonth; { struct tm *tm; time_t Month; time_t Year; time_t ret; if (RelMonth == 0) return 0; tm = localtime(&Start); Month = 12 * tm->tm_year + tm->tm_mon + RelMonth; Year = Month / 12; Month = Month % 12 + 1; ret = Convert(Month, (time_t)tm->tm_mday, Year, (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec, MER24, DSTmaybe); if (ret == -1) return ret; return DSTcorrect(Start, ret); } static int LookupWord(buff) char *buff; { register char *p; register char *q; register const TABLE *tp; int i; int abbrev; /* Make it lowercase. */ for (p = buff; *p; p++) if (isupper((int) *p)) *p = tolower((int) *p); if (strcmp(buff, gettext("am")) == 0 || strcmp(buff, gettext("a.m.")) == 0) { yylval.Meridian = MERam; return tMERIDIAN; } if (strcmp(buff, gettext("pm")) == 0 || strcmp(buff, gettext("p.m.")) == 0) { yylval.Meridian = MERpm; return tMERIDIAN; } /* See if we have an abbreviation for a month. */ if (strlen(buff) == 3) abbrev = 1; else if (strlen(buff) == 4 && buff[3] == '.') { abbrev = 1; buff[3] = '\0'; } else abbrev = 0; for (tp = MonthDayTable; tp->name; tp++) { if (abbrev) { if (strncmp(buff, GETTEXT(tp->name), 3) == 0) { yylval.Number = tp->value; return tp->type; } } else if (strcmp(buff, GETTEXT(tp->name)) == 0) { yylval.Number = tp->value; return tp->type; } } for (tp = TimezoneTable; tp->name; tp++) if (strcmp(buff, GETTEXT(tp->name)) == 0) { yylval.Number = tp->value; return tp->type; } if (strcmp(buff, gettext("dst")) == 0) return tDST; for (tp = UnitsTable; tp->name; tp++) if (strcmp(buff, GETTEXT(tp->name)) == 0) { yylval.Number = tp->value; return tp->type; } /* Strip off any plural and try the units table again. */ i = strlen(buff) - 1; if (buff[i] == 's') { buff[i] = '\0'; for (tp = UnitsTable; tp->name; tp++) if (strcmp(buff, GETTEXT(tp->name)) == 0) { yylval.Number = tp->value; return tp->type; } buff[i] = 's'; /* Put back for "this" in OtherTable. */ } for (tp = OtherTable; tp->name; tp++) if (strcmp(buff, GETTEXT(tp->name)) == 0) { yylval.Number = tp->value; return tp->type; } /* Drop out any periods and try the timezone table again. */ for (i = 0, p = q = buff; *q; q++) if (*q != '.') *p++ = *q; else i++; *p = '\0'; if (i) for (tp = TimezoneTable; tp->name; tp++) if (strcmp(buff, GETTEXT(tp->name)) == 0) { yylval.Number = tp->value; return tp->type; } return tID; } static int yylex() { register char c; register char *p; char buff[20]; int Count; int sign; for ( ; ; ) { while (isspace((int) *yyInput)) yyInput++; c = *yyInput; if (isdigit((int) c) || c == '-' || c == '+') { if (c == '-' || c == '+') { sign = c == '-' ? -1 : 1; if (!isdigit((int) (*++yyInput))) /* skip the '-' sign */ continue; } else sign = 0; for (yylval.Number = 0; isdigit((int) (c = *yyInput++)); ) yylval.Number = 10 * yylval.Number + c - '0'; yyInput--; if (sign < 0) yylval.Number = -yylval.Number; return sign ? tSNUMBER : tUNUMBER; } if (isalpha((int) c)) { for (p = buff; isalpha((int) (c = *yyInput++)) || c == '.'; ) if (p < &buff[sizeof buff - 1]) *p++ = c; *p = '\0'; yyInput--; return LookupWord(buff); } if (c != '(') return *yyInput++; Count = 0; do { c = *yyInput++; if (c == '\0') return c; if (c == '(') Count++; else if (c == ')') Count--; } while (Count > 0); } } #define TM_YEAR_ORIGIN 1900 /* Yield A - B, measured in seconds. */ static time_t difftm(a, b) struct tm *a, *b; { int ay = a->tm_year + (TM_YEAR_ORIGIN - 1); int by = b->tm_year + (TM_YEAR_ORIGIN - 1); return ( ( ( /* difference in day of year */ a->tm_yday - b->tm_yday /* + intervening leap days */ + ((ay >> 2) - (by >> 2)) - (ay/100 - by/100) + ((ay/100 >> 2) - (by/100 >> 2)) /* + difference in years * 365 */ + (time_t)(ay-by) * 365 )*24 + (a->tm_hour - b->tm_hour) )*60 + (a->tm_min - b->tm_min) )*60 + (a->tm_sec - b->tm_sec); } /* For get_date extern declaration compatibility check... yuck. */ #include #include "kadmin.h" time_t get_date(p) char *p; { struct my_timeb *now = NULL; struct tm *tm, gmt; struct my_timeb ftz; time_t Start; time_t tod; time_t delta; yyInput = p; if (now == NULL) { now = &ftz; ftz.time = time((time_t *) 0); if (! (tm = gmtime (&ftz.time))) return -1; gmt = *tm; /* Make a copy, in case localtime modifies *tm. */ ftz.timezone = difftm (&gmt, localtime (&ftz.time)) / 60; } tm = localtime(&now->time); yyYear = tm->tm_year; yyMonth = tm->tm_mon + 1; yyDay = tm->tm_mday; yyTimezone = now->timezone; yyDSTmode = DSTmaybe; yyHour = 0; yyMinutes = 0; yySeconds = 0; yyMeridian = MER24; yyRelSeconds = 0; yyRelMonth = 0; yyHaveDate = 0; yyHaveDay = 0; yyHaveRel = 0; yyHaveTime = 0; yyHaveZone = 0; /* * When yyparse returns, zero or more of yyHave{Time,Zone,Date,Day,Rel} * will have been incremented. The value is number of items of * that type that were found; for all but Rel, more than one is * illegal. * * For each yyHave indicator, the following values are set: * * yyHaveTime: * yyHour, yyMinutes, yySeconds: hh:mm:ss specified, initialized * to zeros above * yyMeridian: MERam, MERpm, or MER24 * yyTimeZone: time zone specified in minutes * yyDSTmode: DSToff if yyTimeZone is set, otherwise unchanged * (initialized above to DSTmaybe) * * yyHaveZone: * yyTimezone: as above * yyDSTmode: DSToff if a non-DST zone is specified, otherwise DSTon * XXX don't understand interaction with yyHaveTime zone info * * yyHaveDay: * yyDayNumber: 0-6 for Sunday-Saturday * yyDayOrdinal: val specified with day ("second monday", * Ordinal=2), otherwise 1 * * yyHaveDate: * yyMonth, yyDay, yyYear: mm/dd/yy specified, initialized to * today above * * yyHaveRel: * yyRelSeconds: seconds specified with MINUTE_UNITs ("3 hours") or * SEC_UNITs ("30 seconds") * yyRelMonth: months specified with MONTH_UNITs ("3 months", "1 * year") * * The code following yyparse turns these values into a single * date stamp. */ if (yyparse() || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1) return -1; /* * If an absolute time specified, set Start to the equivalent Unix * timestamp. Otherwise, set Start to now, and if we do not have * a relatime time (ie: only yyHaveZone), decrement Start to the * beginning of today. * * By having yyHaveDay in the "absolute" list, "next Monday" means * midnight next Monday. Otherwise, "next Monday" would mean the * time right now, next Monday. It's not clear to me why the * current behavior is preferred. */ if (yyHaveDate || yyHaveTime || yyHaveDay) { Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds, yyMeridian, yyDSTmode); if (Start < 0) return -1; } else { Start = now->time; if (!yyHaveRel) Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec; } /* * Add in the relative time specified. RelativeMonth adds in the * months, accounting for the fact that the actual length of "3 * months" depends on where you start counting. * * XXX By having this separate from the previous block, we are * allowing dates like "10:00am 3 months", which means 3 months * from 10:00am today, or even "1/1/99 two days" which means two * days after 1/1/99. * * XXX Shouldn't this only be done if yyHaveRel, just for * thoroughness? */ Start += yyRelSeconds; delta = RelativeMonth(Start, yyRelMonth); if (delta == (time_t) -1) return -1; Start += delta; /* * Now, if you specified a day of week and counter, add it in. By * disallowing Date but allowing Time, you can say "5pm next * monday". * * XXX The yyHaveDay && !yyHaveDate restriction should be enforced * above and be able to cause failure. */ if (yyHaveDay && !yyHaveDate) { tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber); Start += tod; } /* Have to do *something* with a legitimate -1 so it's distinguishable * from the error return value. (Alternately could set errno on error.) */ return Start == -1 ? 0 : Start; } #if defined(TEST) /* ARGSUSED */ main(ac, av) int ac; char *av[]; { char buff[128]; time_t d; (void)printf(gettext("Enter date, or blank line to exit.\n\t> ")); (void)fflush(stdout); while (gets(buff) && buff[0]) { d = get_date(buff, (struct my_timeb *)NULL); if (d == -1) (void)printf( gettext("Bad format - couldn't convert.\n")); else (void)printf("%s", ctime(&d)); (void)printf("\t> "); (void)fflush(stdout); } exit(0); /* NOTREACHED */ } #endif /* defined(TEST) */ #!/bin/sh # # # Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # TEXTDOMAIN=SUNW_OST_OSCMD export TEXTDOMAIN # list_princs keytab # returns a list of principals in the keytab # sorted and uniquified list_princs() { klist -k $keytab | tail +4 | awk '{print $2}' | sort | uniq } set_command() { if [ x$command != x ] ; then cmd_error `gettext "Only one command can be specified"` usage exit 1 fi command=$1 } #interactive_prompt prompt princ # If in interactive mode return true if the principal should be acted on # otherwise return true all the time # # SUNW14resync: If in interactive mode the default is now to return false # i.e. if in interactive mode unless the user types "Yes" or # "yes" false will be returned. # interactive_prompt() { if [ $interactive = 0 ] ; then return 0 fi PROMPT=`gettext "%s for %s? [yes no] "` Y1=`gettext "yes"` Y2=`gettext "Yes"` printf "$PROMPT" "$1" "$2" read ans case $ans in ${Y1}|${Y2}) return 0 ;; esac return 1 } cmd_error() { echo $@ 2>&1 } usage() { USAGE=`gettext "Usage: $0 [-i] [-f file] list|change|delete|delold"` echo $USAGE } change_key() { princs=`list_princs ` for princ in $princs; do ACTION=`gettext "Change key"` if interactive_prompt "$ACTION" $princ; then kadmin -k -t $keytab -p $princ -q "ktadd -k $keytab $princ" fi done } delete_old_keys() { princs=`list_princs ` for princ in $princs; do ACTION=`gettext "Delete old keys"` if interactive_prompt "$ACTION" $princ; then kadmin -k -t $keytab -p $princ -q "ktrem -k $keytab $princ old" fi done } delete_keys() { interactive=1 princs=`list_princs ` for princ in $princs; do ACTION=`gettext "Delete all keys"` if interactive_prompt "$ACTION" $princ; then kadmin -p $princ -k -t $keytab -q "ktrem -k $keytab $princ all" fi done } keytab=/etc/krb5/krb5.keytab interactive=0 CHANGE=`gettext "change"` DELOLD=`gettext "delold"` DELETE=`gettext "delete"` LIST=`gettext "list"` while [ $# -gt 0 ] ; do opt=$1 shift case $opt in "-f") keytab=$1 shift ;; "-i") interactive=1 ;; ${CHANGE}|${DELOLD}|${DELETE}|${LIST}) set_command $opt ;; *) ILLEGAL=`gettext "Illegal option: "` cmd_error $ILLEGAL $opt usage exit 1 ;; esac done case $command in $CHANGE) change_key ;; $DELOLD) delete_old_keys ;; $DELETE) delete_keys ;; $LIST) klist -k $keytab ;; *) usage ;; esac /* * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. */ /* * Copyright 1994 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. * * kadmin.c: base functions for a kadmin command line interface using * the OVSecure library */ #include #include #include #include #include #include #include #include /* #include */ #include #include "kadmin.h" #include #include /* * Solaris: the following are needed for paging */ #include #include /* command name when called "locally" (i.e. non-networked client ) */ #define KADMIN_LOCAL_NAME "kadmin.local" /* functions defined in remote/local specific files */ extern void usage(const char *); /* special struct to convert flag names for principals to actual krb5_flags for a principal */ struct pflag { char *flagname; /* name of flag as typed to CLI */ int flaglen; /* length of string (not counting -,+) */ krb5_flags theflag; /* actual principal flag to set/clear */ int set; /* 0 means clear, 1 means set (on '-') */ }; static struct pflag flags[] = { {"allow_postdated", 15, KRB5_KDB_DISALLOW_POSTDATED, 1}, {"allow_forwardable", 17, KRB5_KDB_DISALLOW_FORWARDABLE, 1}, {"allow_tgs_req", 13, KRB5_KDB_DISALLOW_TGT_BASED, 1}, {"allow_renewable", 15, KRB5_KDB_DISALLOW_RENEWABLE, 1}, {"allow_proxiable", 15, KRB5_KDB_DISALLOW_PROXIABLE, 1}, {"allow_dup_skey", 14, KRB5_KDB_DISALLOW_DUP_SKEY, 1}, {"allow_tix", 9, KRB5_KDB_DISALLOW_ALL_TIX, 1}, {"requires_preauth", 16, KRB5_KDB_REQUIRES_PRE_AUTH, 0}, {"requires_hwauth", 15, KRB5_KDB_REQUIRES_HW_AUTH, 0}, {"needchange", 10, KRB5_KDB_REQUIRES_PWCHANGE, 0}, {"allow_svr", 9, KRB5_KDB_DISALLOW_SVR, 1}, {"password_changing_service", 25, KRB5_KDB_PWCHANGE_SERVICE, 0 }, {"support_desmd5", 14, KRB5_KDB_SUPPORT_DESMD5, 0 } }; static char *prflags[] = { "DISALLOW_POSTDATED", /* 0x00000001 */ "DISALLOW_FORWARDABLE", /* 0x00000002 */ "DISALLOW_TGT_BASED", /* 0x00000004 */ "DISALLOW_RENEWABLE", /* 0x00000008 */ "DISALLOW_PROXIABLE", /* 0x00000010 */ "DISALLOW_DUP_SKEY", /* 0x00000020 */ "DISALLOW_ALL_TIX", /* 0x00000040 */ "REQUIRES_PRE_AUTH", /* 0x00000080 */ "REQUIRES_HW_AUTH", /* 0x00000100 */ "REQUIRES_PWCHANGE", /* 0x00000200 */ "UNKNOWN_0x00000400", /* 0x00000400 */ "UNKNOWN_0x00000800", /* 0x00000800 */ "DISALLOW_SVR", /* 0x00001000 */ "PWCHANGE_SERVICE", /* 0x00002000 */ "SUPPORT_DESMD5", /* 0x00004000 */ "NEW_PRINC", /* 0x00008000 */ }; char *getenv(); int exit_status = 0; char *def_realm = NULL; char *whoami = NULL; void *handle = NULL; krb5_context context; char *ccache_name = NULL; int locked = 0; static char *strdur(duration) time_t duration; { static char out[50]; int neg, days, hours, minutes, seconds; if (duration < 0) { duration *= -1; neg = 1; } else neg = 0; days = duration / (24 * 3600); duration %= 24 * 3600; hours = duration / 3600; duration %= 3600; minutes = duration / 60; duration %= 60; seconds = duration; snprintf(out, sizeof (out), "%s%d %s %02d:%02d:%02d", neg ? "-" : "", days, days == 1 ? gettext("day") : gettext("days"), hours, minutes, seconds); return out; } static char *strdate(when) krb5_timestamp when; { struct tm *tm; static char out[40]; time_t lcltim = when; tm = localtime(&lcltim); strftime(out, sizeof(out), gettext("%a %b %d %H:%M:%S %Z %Y"), tm); return out; } /* this is a wrapper to go around krb5_parse_principal so we can set the default realm up properly */ static krb5_error_code kadmin_parse_name(name, principal) char *name; krb5_principal *principal; { char *cp, *fullname; krb5_error_code retval; if (name == NULL) return (EINVAL); /* assumes def_realm is initialized! */ fullname = (char *)malloc(strlen(name) + 1 + strlen(def_realm) + 1); if (fullname == NULL) return ENOMEM; strcpy(fullname, name); cp = strchr(fullname, '@'); while (cp) { if (cp - fullname && *(cp - 1) != '\\') break; else cp = strchr(cp + 1, '@'); } if (cp == NULL) { strcat(fullname, "@"); strcat(fullname, def_realm); } retval = krb5_parse_name(context, fullname, principal); free(fullname); return retval; } static void extended_com_err_fn (const char *myprog, errcode_t code, const char *fmt, va_list args) { if (code) { const char *emsg; emsg = krb5_get_error_message (context, code); fprintf (stderr, "%s: %s ", myprog, emsg); krb5_free_error_message (context, emsg); } else { fprintf (stderr, "%s: ", myprog); } vfprintf (stderr, fmt, args); fprintf (stderr, "\n"); } char *kadmin_startup(argc, argv) int argc; char *argv[]; { extern char *optarg; char *princstr = NULL, *keytab_name = NULL, *query = NULL; char *password = NULL; char *luser, *canon, *cp; int optchar, freeprinc = 0, use_keytab = 0; struct passwd *pw; kadm5_ret_t retval; krb5_ccache cc; krb5_principal princ; kadm5_config_params params; char **db_args = NULL; int db_args_size = 0; char *db_name = NULL; char *svcname = NULL; memset((char *) ¶ms, 0, sizeof(params)); if (strcmp (whoami, "kadmin.local") == 0) set_com_err_hook(extended_com_err_fn); retval = kadm5_init_krb5_context(&context); if (retval) { com_err(whoami, retval, gettext("while initializing krb5 library")); exit(1); } while ((optchar = getopt(argc, argv, "x:r:p:kq:w:d:s:mc:t:e:ON")) != EOF) { switch (optchar) { case 'x': db_args_size++; { char **temp = realloc(db_args, sizeof(char*) * (db_args_size+1)); if (temp == NULL) { fprintf(stderr, gettext("%s: Cannot initialize. Not enough memory\n"), argv[0]); exit(1); } db_args = temp; } db_args[db_args_size-1] = optarg; db_args[db_args_size] = NULL; break; case 'r': def_realm = optarg; break; case 'p': princstr = optarg; break; case 'c': ccache_name = optarg; break; case 'k': use_keytab++; break; case 't': keytab_name = optarg; break; case 'w': password = optarg; break; case 'q': query = optarg; break; case 'd': /* now db_name is not a seperate argument. It has to be passed as part of the db_args */ if (!db_name) { db_name = malloc(strlen(optarg) + sizeof("dbname=")); } else { db_name = realloc(db_name, strlen(optarg) + sizeof("dbname=")); } strcpy(db_name, "dbname="); strcat(db_name, optarg); db_args_size++; { char **temp = realloc(db_args, sizeof(char*) * (db_args_size+1)); /* one for NULL */ if (temp == NULL) { fprintf(stderr, gettext("%s: Cannot initialize. Not enough memory\n"), argv[0]); exit(1); } db_args = temp; } db_args[db_args_size-1] = db_name; db_args[db_args_size] = NULL; break; case 's': params.admin_server = optarg; params.mask |= KADM5_CONFIG_ADMIN_SERVER; break; case 'm': params.mkey_from_kbd = 1; params.mask |= KADM5_CONFIG_MKEY_FROM_KBD; break; case 'e': retval = krb5_string_to_keysalts(optarg, ", \t", ":.-", 0, ¶ms.keysalts, ¶ms.num_keysalts); if (retval) { com_err(whoami, retval, gettext("while parsing keysalts %s"), optarg); exit(1); } params.mask |= KADM5_CONFIG_ENCTYPES; break; case 'O': /* Undocumented option for testing only */ svcname = KADM5_ADMIN_SERVICE_P; break; default: usage(whoami); } } if ((ccache_name && use_keytab) || (keytab_name && !use_keytab)) usage(whoami); if (def_realm == NULL && krb5_get_default_realm(context, &def_realm)) { if (freeprinc) free(princstr); fprintf(stderr, gettext("%s: unable to get default realm\n"), whoami); exit(1); } params.mask |= KADM5_CONFIG_REALM; params.realm = def_realm; if (svcname == NULL) { if (kadm5_get_adm_host_srv_name(context, def_realm, &svcname)) { fprintf(stderr, gettext("%s: unable to get host based " "service name for realm %s\n"), whoami, def_realm); if (freeprinc) free(princstr); exit(1); } } /* * Set cc to an open credentials cache, either specified by the -c * argument or the default. */ if (ccache_name == NULL) { if ((retval = krb5_cc_default(context, &cc))) { com_err(whoami, retval, gettext("while opening default " "credentials cache")); exit(1); } } else { if ((retval = krb5_cc_resolve(context, ccache_name, &cc))) { com_err(whoami, retval, gettext("while opening credentials cache %s"), ccache_name); exit(1); } } /* * If no principal name is specified: If a ccache was specified * and its primary principal name can be read, it is used, else if * a keytab was specified, the principal name is host/hostname, * otherwise append "/admin" to the primary name of the default * ccache, $USER, or pw_name. * * Gee, 100+ lines to figure out the client principal name. This * should be compressed... */ if (princstr == NULL) { if (ccache_name != NULL && !krb5_cc_get_principal(context, cc, &princ)) { if ((retval = krb5_unparse_name(context, princ, &princstr))) { com_err(whoami, retval, gettext("while canonicalizing principal name")); krb5_free_principal(context, princ); exit(1); } krb5_free_principal(context, princ); freeprinc++; } else if (use_keytab != 0) { if ((retval = krb5_sname_to_principal(context, NULL, "host", KRB5_NT_SRV_HST, &princ))) { com_err(whoami, retval, gettext("creating host service principal")); exit(1); } if ((retval = krb5_unparse_name(context, princ, &princstr))) { com_err(whoami, retval, gettext("while canonicalizing principal name")); krb5_free_principal(context, princ); exit(1); } krb5_free_principal(context, princ); freeprinc++; } else if (!krb5_cc_get_principal(context, cc, &princ)) { char *realm = NULL; if (krb5_unparse_name(context, princ, &canon)) { fprintf(stderr, gettext("%s: unable to canonicalize " "principal\n"), whoami); krb5_free_principal(context, princ); exit(1); } /* strip out realm of principal if it's there */ realm = strchr(canon, '@'); while (realm) { if (realm - canon && *(realm - 1) != '\\') break; else realm = strchr(realm+1, '@'); } if (realm) *realm++ = '\0'; cp = strchr(canon, '/'); while (cp) { if (cp - canon && *(cp - 1) != '\\') break; else cp = strchr(cp+1, '/'); } if (cp != NULL) *cp = '\0'; princstr = (char*)malloc(strlen(canon) + 6 /* "/admin" */ + (realm ? 1 + strlen(realm) : 0) + 1); if (princstr == NULL) { fprintf(stderr, gettext("%s: out of memory\n"), whoami); exit(1); } strcpy(princstr, canon); strcat(princstr, "/admin"); if (realm) { strcat(princstr, "@"); strcat(princstr, realm); } free(canon); krb5_free_principal(context, princ); freeprinc++; } else if ((luser = getenv("USER"))) { princstr = (char *) malloc(strlen(luser) + 7 /* "/admin@" */ + strlen(def_realm) + 1); if (princstr == NULL) { fprintf(stderr, gettext("%s: out of memory\n"), whoami); exit(1); } strcpy(princstr, luser); strcat(princstr, "/admin"); strcat(princstr, "@"); strcat(princstr, def_realm); freeprinc++; } else if ((pw = getpwuid(getuid()))) { princstr = (char *) malloc(strlen(pw->pw_name) + 7 /* "/admin@" */ + strlen(def_realm) + 1); if (princstr == NULL) { fprintf(stderr, gettext("%s: out of memory\n"), whoami); exit(1); } strcpy(princstr, pw->pw_name); strcat(princstr, "/admin@"); strcat(princstr, def_realm); freeprinc++; } else { fprintf(stderr, gettext("%s: unable to figure out " "a principal name\n"), whoami); exit(1); } } retval = krb5_klog_init(context, "admin_server", whoami, 0); if (retval) { com_err(whoami, retval, "while setting up logging"); exit(1); } /* * Initialize the kadm5 connection. If we were given a ccache, * use it. Otherwise, use/prompt for the password. */ /* Solaris Kerberos: * Send warnings to stderr */ if (ccache_name) { fprintf(stderr, gettext("Authenticating as principal %s with existing credentials.\n"), princstr); retval = kadm5_init_with_creds(princstr, cc, svcname, ¶ms, KADM5_STRUCT_VERSION, KADM5_API_VERSION_2, db_args, &handle); } else if (use_keytab) { if (keytab_name) fprintf(stderr, gettext("Authenticating as principal %s with keytab %s.\n"), princstr, keytab_name); else fprintf(stderr, gettext("Authenticating as principal %s with default keytab.\n"), princstr); retval = kadm5_init_with_skey(princstr, keytab_name, svcname, ¶ms, KADM5_STRUCT_VERSION, KADM5_API_VERSION_2, db_args, &handle); } else { fprintf(stderr, gettext("Authenticating as principal %s with password.\n"), princstr); retval = kadm5_init_with_password(princstr, password, svcname, ¶ms, KADM5_STRUCT_VERSION, KADM5_API_VERSION_2, db_args, &handle); } if (retval) { if (retval == KADM5_RPC_ERROR_CANTENCODEARGS || retval == KADM5_RPC_ERROR_CANTDECODEARGS) { com_err(whoami, KADM5_RPC_ERROR, gettext("while initializing %s interface"), whoami); /* privacy-enabled mech probably not installed/configed */ com_err(whoami, retval, gettext("."), whoami); } else { com_err(whoami, retval, gettext("while initializing %s interface"), whoami); if (retval == KADM5_BAD_CLIENT_PARAMS || retval == KADM5_BAD_SERVER_PARAMS) usage(whoami); } exit(1); } if (freeprinc) free(princstr); if (db_name) free(db_name), db_name=NULL; if (db_args) free(db_args), db_args=NULL; if ((retval = krb5_cc_close(context, cc))) { com_err(whoami, retval, gettext("while closing ccache %s"), ccache_name); exit(1); } /* register the WRFILE keytab type and set it as the default */ { #define DEFAULT_KEYTAB "WRFILE:/etc/krb5/krb5.keytab" /* XXX krb5_defkeyname is an internal library global and should go away */ extern char *krb5_defkeyname; krb5_defkeyname = DEFAULT_KEYTAB; } if ((retval = kadm5_init_iprop(handle)) != 0) { com_err(whoami, retval, gettext("while mapping update log")); exit(1); } /* Solaris kerberos: fix memory leak */ if (svcname) free(svcname); return query; } int quit() { kadm5_ret_t retval; if (locked) { retval = kadm5_unlock(handle); if (retval) { com_err("quit", retval, gettext("while unlocking locked database")); return 1; } locked = 0; } kadm5_destroy(handle); if (ccache_name != NULL) { fprintf(stderr, gettext("\n\a\a\aAdministration credentials " "NOT DESTROYED.\n")); } /* insert more random cleanup here */ krb5_klog_close(context); krb5_free_context(context); context = NULL; return 0; } void kadmin_lock(argc, argv) int argc; char *argv[]; { kadm5_ret_t retval; if (locked) return; retval = kadm5_lock(handle); if (retval) { com_err("lock", retval, ""); return; } locked = 1; } void kadmin_unlock(argc, argv) int argc; char *argv[]; { kadm5_ret_t retval; if (!locked) return; retval = kadm5_unlock(handle); if (retval) { com_err("unlock", retval, ""); return; } locked = 0; } void kadmin_delprinc(argc, argv) int argc; char *argv[]; { kadm5_ret_t retval; krb5_principal princ; char *canon; char reply[32]; if (! (argc == 2 || (argc == 3 && !strcmp("-force", argv[1])))) { fprintf(stderr, "%s: delete_principal [-force] %s\n", gettext("usage"), gettext("principal")); return; } retval = kadmin_parse_name(argv[argc - 1], &princ); if (retval) { com_err("delete_principal", retval, gettext("while parsing principal name")); return; } retval = krb5_unparse_name(context, princ, &canon); if (retval) { com_err("delete_principal", retval, gettext("while canonicalizing principal")); krb5_free_principal(context, princ); return; } if (argc == 2) { printf(gettext("Are you sure you want to delete " "the principal \"%s\"? (yes/no): "), canon); fgets(reply, sizeof (reply), stdin); if (strncmp(gettext("yes\n"), reply, sizeof (reply)) && strncmp(gettext("y\n"), reply, sizeof (reply)) && strncmp(gettext("Y\n"), reply, sizeof (reply))) { fprintf(stderr, gettext("Principal \"%s\" not deleted\n"), canon); free(canon); krb5_free_principal(context, princ); return; } } retval = kadm5_delete_principal(handle, princ); krb5_free_principal(context, princ); if (retval) { com_err("delete_principal", retval, gettext("while deleting principal \"%s\""), canon); free(canon); return; } printf(gettext("Principal \"%s\" deleted.\n"), canon); printf(gettext("Make sure that you have removed this principal " "from all ACLs before reusing.\n")); free(canon); return; } void kadmin_cpw(argc, argv) int argc; char *argv[]; { kadm5_ret_t retval; static char newpw[1024]; static char prompt1[1024], prompt2[1024]; char *canon; char *pwarg = NULL; int n_ks_tuple = 0, randkey = 0; krb5_boolean keepold = FALSE; krb5_key_salt_tuple *ks_tuple = NULL; krb5_principal princ; char **db_args = NULL; int db_args_size = 0; int local_kadmin = 0; local_kadmin = (strcmp(whoami, KADMIN_LOCAL_NAME) == 0); if (argc < 2) { goto usage; } for (argv++, argc--; argc > 1; argc--, argv++) { if (!strcmp("-x", *argv)) { argc--; if (argc < 1) { fprintf(stderr, gettext("change_password: missing db argument\n")); goto usage; } db_args_size++; { char **temp = realloc(db_args, sizeof(char*) * (db_args_size+1)); /* one for NULL */ if (temp == NULL) { fprintf(stderr, gettext("change_password: Not enough memory\n")); free(db_args), db_args = NULL; exit(1); } db_args = temp; } db_args[db_args_size-1] = *++argv; db_args[db_args_size] = NULL; continue; } if (!strcmp("-pw", *argv)) { argc--; if (argc < 1) { fprintf(stderr, "change_password: %s", gettext("missing password arg\n")); goto usage; } pwarg = *++argv; continue; } if (!strcmp("-randkey", *argv)) { randkey++; continue; } if (!strcmp("-keepold", *argv)) { keepold = TRUE; continue; } if (!strcmp("-e", *argv)) { argc--; if (argc < 1) { fprintf(stderr, "change_password: %s", gettext("missing keysaltlist arg\n")); goto usage; } retval = krb5_string_to_keysalts(*++argv, ", \t", ":.-", 0, &ks_tuple, &n_ks_tuple); if (retval) { com_err("change_password", retval, gettext("while parsing keysalts %s"), *argv); return; } continue; } goto usage; } if (*argv == NULL) { com_err("change_password", 0, "missing principal name"); goto usage; } retval = kadmin_parse_name(*argv, &princ); if (retval) { com_err("change_password", retval, gettext("while parsing principal name")); if (ks_tuple != NULL) free(ks_tuple); if (db_args) free(db_args); goto usage; } retval = krb5_unparse_name(context, princ, &canon); if (retval) { com_err("change_password", retval, gettext("while canonicalizing principal")); krb5_free_principal(context, princ); if (ks_tuple != NULL) free(ks_tuple); if (db_args) free(db_args); return; } if (pwarg != NULL) { if (keepold || ks_tuple != NULL) { retval = kadm5_chpass_principal_3(handle, princ, keepold, n_ks_tuple, ks_tuple, pwarg); if (ks_tuple != NULL) free(ks_tuple); } else { retval = kadm5_chpass_principal(handle, princ, pwarg); } krb5_free_principal(context, princ); if (retval) { com_err("change_password", retval, gettext("while changing password for \"%s\"."), canon); free(canon); if (db_args) free(db_args); return; } printf(gettext("Password for \"%s\" changed.\n"), canon); free(canon); if (db_args) free(db_args); return; } else if (randkey) { if (keepold || ks_tuple != NULL || local_kadmin) { retval = kadm5_randkey_principal_3(handle, princ, keepold, n_ks_tuple, ks_tuple, NULL, NULL); if (ks_tuple != NULL) free(ks_tuple); } else { retval = kadm5_randkey_principal(handle, princ, NULL, NULL); } krb5_free_principal(context, princ); if (retval) { com_err("change_password", retval, gettext("while randomizing key for \"%s\"."), canon); free(canon); if (db_args) free(db_args); return; } printf(gettext("Key for \"%s\" randomized.\n"), canon); free(canon); if (db_args) free(db_args); return; } else if (argc == 1) { unsigned int i = sizeof (newpw) - 1; snprintf(prompt1, sizeof (prompt1), gettext("Enter password for principal \"%.900s\""), *argv); snprintf(prompt2, sizeof (prompt2), gettext("Re-enter password for principal \"%.900s\""), *argv); retval = krb5_read_password(context, prompt1, prompt2, newpw, &i); if (retval) { com_err("change_password", retval, gettext("while reading password for \"%s\"."), canon); free(canon); if (ks_tuple != NULL) free(ks_tuple); krb5_free_principal(context, princ); if (db_args) free(db_args); return; } if (keepold || ks_tuple != NULL) { retval = kadm5_chpass_principal_3(handle, princ, keepold, n_ks_tuple, ks_tuple, newpw); if (ks_tuple != NULL) free(ks_tuple); } else { retval = kadm5_chpass_principal(handle, princ, newpw); } krb5_free_principal(context, princ); memset(newpw, 0, sizeof (newpw)); if (retval) { com_err("change_password", retval, gettext("while changing password for \"%s\"."), canon); free(canon); if (db_args) free(db_args); return; } printf(gettext("Password for \"%s\" changed.\n"), canon); free(canon); if (db_args) free(db_args); return; } else { free(canon); krb5_free_principal(context, princ); usage: if (ks_tuple != NULL) free(ks_tuple); fprintf(stderr, "%s: change_password [-randkey] [-keepold] " "[-e keysaltlist] [-pw password] %s\n", gettext("usage"), gettext("principal")); return; } } static void kadmin_free_tl_data(kadm5_principal_ent_t princ) { krb5_tl_data *tl_data = princ->tl_data; int n_tl_data = princ->n_tl_data; int i; princ->n_tl_data = 0; princ->tl_data = NULL; for (i = 0; tl_data && (i < n_tl_data); i++) { krb5_tl_data *next = tl_data->tl_data_next; if (tl_data->tl_data_contents) free(tl_data->tl_data_contents); free(tl_data); tl_data = next; } } #define KRB5_TL_DB_ARGS 0x7fff static int kadmin_parse_princ_args(argc, argv, oprinc, mask, pass, randkey, ks_tuple, n_ks_tuple, caller) int argc; char *argv[]; kadm5_principal_ent_t oprinc; long *mask; char **pass; int *randkey; krb5_key_salt_tuple **ks_tuple; int *n_ks_tuple; char *caller; { int i, j, attrib_set; time_t date; time_t now; krb5_error_code retval; krb5_tl_data *tl_data, *tail = NULL; *mask = 0; *pass = NULL; *n_ks_tuple = 0; *ks_tuple = NULL; time(&now); *randkey = 0; for (i = 1; i < argc - 1; i++) { attrib_set = 0; if (strlen(argv[i]) == 2 && !strcmp("-x",argv[i])) { if (++i > argc - 2) return -1; tl_data = malloc(sizeof(krb5_tl_data)); if (tl_data == NULL) { fprintf(stderr, gettext("Not enough memory\n")); return ENOMEM; } memset(tl_data, 0, sizeof(krb5_tl_data)); tl_data->tl_data_type = KRB5_TL_DB_ARGS; tl_data->tl_data_length = strlen(argv[i])+1; tl_data->tl_data_contents = (unsigned char*)strdup(argv[i]); if (tail) { tail->tl_data_next = tl_data; } else { oprinc->tl_data = tl_data; } tail = tl_data; oprinc->n_tl_data++; if (tl_data->tl_data_contents == NULL) { fprintf(stderr, gettext("Not enough memory\n")); return ENOMEM; } *mask |= KADM5_TL_DATA; continue; } if (strlen(argv[i]) == 7 && !strcmp("-expire", argv[i])) { if (++i > argc - 2) return -1; else { date = get_date(argv[i]); if (date == (time_t)-1) { fprintf(stderr, gettext("Invalid date " "specification " "\"%s\".\n"), argv[i]); return -1; } oprinc->princ_expire_time = date; *mask |= KADM5_PRINC_EXPIRE_TIME; continue; } } if (strlen(argv[i]) == 9 && !strcmp("-pwexpire", argv[i])) { if (++i > argc - 2) return -1; else { date = get_date(argv[i]); if (date == (time_t)-1) { fprintf(stderr, gettext("Invalid date " "specification " "\"%s\".\n"), argv[i]); return -1; } oprinc->pw_expiration = date; *mask |= KADM5_PW_EXPIRATION; continue; } } if (strlen(argv[i]) == 8 && !strcmp("-maxlife", argv[i])) { if (++i > argc - 2) return -1; else { date = get_date(argv[i]); if (date == (time_t)-1) { fprintf(stderr, gettext("Invalid date " "specification " "\"%s\".\n"), argv[i]); return -1; } oprinc->max_life = date - now; *mask |= KADM5_MAX_LIFE; continue; } } if (strlen(argv[i]) == 13 && !strcmp("-maxrenewlife", argv[i])) { if (++i > argc - 2) return -1; else { date = get_date(argv[i]); if (date == (time_t)-1) { fprintf(stderr, gettext("Invalid date " "specification " "\"%s\".\n"), argv[i]); return -1; } oprinc->max_renewable_life = date - now; *mask |= KADM5_MAX_RLIFE; continue; } } if (strlen(argv[i]) == 5 && !strcmp("-kvno", argv[i])) { if (++i > argc - 2) return -1; else { oprinc->kvno = atoi(argv[i]); *mask |= KADM5_KVNO; continue; } } if (strlen(argv[i]) == 7 && !strcmp("-policy", argv[i])) { if (++i > argc - 2) return -1; else { oprinc->policy = argv[i]; *mask |= KADM5_POLICY; continue; } } if (strlen(argv[i]) == 12 && !strcmp("-clearpolicy", argv[i])) { oprinc->policy = NULL; *mask |= KADM5_POLICY_CLR; continue; } if (strlen(argv[i]) == 3 && !strcmp("-pw", argv[i])) { if (++i > argc - 2) return -1; else { *pass = argv[i]; continue; } } if (strlen(argv[i]) == 8 && !strcmp("-randkey", argv[i])) { ++*randkey; continue; } if (!strcmp("-e", argv[i])) { if (++i > argc - 2) return -1; else { retval = krb5_string_to_keysalts(argv[i], ", \t", ":.-", 0, ks_tuple, n_ks_tuple); if (retval) { com_err(caller, retval, gettext("while parsing keysalts %s"), argv[i]); return -1; } } continue; } for (j = 0; j < sizeof (flags) / sizeof (struct pflag); j++) { if (strlen(argv[i]) == flags[j].flaglen + 1 && !strcmp(flags[j].flagname, &argv[i][1] /* strip off leading + or - */)) { if ((flags[j].set && argv[i][0] == '-') || (!flags[j].set && argv[i][0] == '+')) { oprinc->attributes |= flags[j].theflag; *mask |= KADM5_ATTRIBUTES; attrib_set++; break; } else if ((flags[j].set && argv[i][0] == '+') || (!flags[j].set && argv[i][0] == '-')) { oprinc->attributes &= ~flags[j].theflag; *mask |= KADM5_ATTRIBUTES; attrib_set++; break; } else { return -1; } } } if (!attrib_set) return -1; /* nothing was parsed */ } if (i != argc - 1) { return -1; } retval = kadmin_parse_name(argv[i], &oprinc->principal); if (retval) { com_err(caller, retval, gettext("while parsing principal")); return -1; } return 0; } static void kadmin_addprinc_usage(func) char *func; { fprintf(stderr, "%s: %s %s\n", gettext("usage"), func, gettext("[options] principal")); fprintf(stderr, gettext("\toptions are:\n")); fprintf(stderr, "\t\t[-expire expdate] [-pwexpire pwexpdate] " "[-maxlife maxtixlife]\n\t\t[-kvno kvno] [-policy policy] " "[-randkey] [-pw password]\n\t\t[-maxrenewlife maxrenewlife] " "[-e keysaltlist] [{+|-}attribute]\n"); fprintf(stderr, gettext("\tattributes are:\n")); fprintf(stderr, "%s%s%s", "\t\tallow_postdated allow_forwardable allow_tgs_req " "allow_renewable\n", "\t\tallow_proxiable allow_dup_skey allow_tix " "requires_preauth\n", "\t\trequires_hwauth needchange allow_svr " "password_changing_service\n"); } static void kadmin_modprinc_usage(func) char *func; { fprintf(stderr, "%s: %s %s\n", gettext("usage"), func, gettext("[options] principal")); fprintf(stderr, gettext("\toptions are:\n")); fprintf(stderr, "\t\t[-expire expdate] [-pwexpire pwexpdate] " "[-maxlife maxtixlife]\n\t\t[-kvno kvno] [-policy policy] " "[-clearpolicy]\n\t\t[-maxrenewlife maxrenewlife] " "[{+|-}attribute]\n"); fprintf(stderr, gettext("\tattributes are:\n")); fprintf(stderr, "%s%s%s", "\t\tallow_postdated allow_forwardable allow_tgs_req " "allow_renewable\n", "\t\tallow_proxiable allow_dup_skey allow_tix " "requires_preauth\n", "\t\trequires_hwauth needchange allow_svr " "password_changing_service\n"); } void kadmin_addprinc(argc, argv) int argc; char *argv[]; { kadm5_principal_ent_rec princ, dprinc; kadm5_policy_ent_rec defpol; long mask; int randkey = 0, i; int n_ks_tuple; krb5_key_salt_tuple *ks_tuple; char *pass, *canon; krb5_error_code retval; static char newpw[1024], dummybuf[256]; static char prompt1[1024], prompt2[1024]; int local_kadmin = 0; local_kadmin = (strcmp(whoami, KADMIN_LOCAL_NAME) == 0); if (dummybuf[0] == 0) { for (i = 0; i < 256; i++) dummybuf[i] = (i+1) % 256; } /* Zero all fields in request structure */ memset(&princ, 0, sizeof(princ)); memset(&dprinc, 0, sizeof(dprinc)); princ.attributes = dprinc.attributes = 0; if (kadmin_parse_princ_args(argc, argv, &princ, &mask, &pass, &randkey, &ks_tuple, &n_ks_tuple, "add_principal")) { kadmin_addprinc_usage("add_principal"); kadmin_free_tl_data(&princ); /* need to free ks_tuple also??? */ return; } retval = krb5_unparse_name(context, princ.principal, &canon); if (retval) { com_err("add_principal", retval, gettext("while canonicalizing principal")); krb5_free_principal(context, princ.principal); if (ks_tuple != NULL) free(ks_tuple); kadmin_free_tl_data(&princ); return; } /* * If -policy was not specified, and -clearpolicy was not * specified, and the policy "default" exists, assign it. If * -clearpolicy was specified, then KADM5_POLICY_CLR should be * unset, since it is never valid for kadm5_create_principal. */ if ((! (mask & KADM5_POLICY)) && (! (mask & KADM5_POLICY_CLR))) { if (! kadm5_get_policy(handle, "default", &defpol)) { fprintf(stderr, gettext("NOTICE: no policy specified for %s; assigning \"default\"\n"), canon); princ.policy = "default"; mask |= KADM5_POLICY; (void) kadm5_free_policy_ent(handle, &defpol); } else fprintf(stderr, gettext("WARNING: no policy specified for %s; defaulting to no policy\n"), canon); } mask &= ~KADM5_POLICY_CLR; /* * Set 'notix' for randkey principals and also for principals which have * specified flag options on the cmdline. This is because we want to apply * generic flag settings from 'default_principal_flags' first (during * principal creation), followed by a kadm5_modify_principal() which * correctly applies the cli flag options. So, we do *not* want any tix * issued in the interim. */ if (randkey || (mask & KADM5_ATTRIBUTES)) princ.attributes |= KRB5_KDB_DISALLOW_ALL_TIX; if (randkey) { mask |= KADM5_ATTRIBUTES; pass = dummybuf; } else if (pass == NULL) { unsigned int sz = sizeof (newpw) - 1; snprintf(prompt1, sizeof (prompt1), gettext("Enter password for principal \"%.900s\""), canon); snprintf(prompt2, sizeof (prompt1), gettext("Re-enter password for principal \"%.900s\""), canon); retval = krb5_read_password(context, prompt1, prompt2, newpw, &sz); if (retval) { com_err("add_principal", retval, gettext("while reading password for \"%s\"."), canon); free(canon); krb5_free_principal(context, princ.principal); kadmin_free_tl_data(&princ); return; } pass = newpw; } mask |= KADM5_PRINCIPAL; /* * If the client being used is local, always use the new * API so we get the full set of enctype support. */ if (ks_tuple != NULL || local_kadmin) { retval = kadm5_create_principal_3(handle, &princ, mask, n_ks_tuple, ks_tuple, pass); } else { retval = kadm5_create_principal(handle, &princ, mask, pass); } if (retval) { com_err("add_principal", retval, gettext("while creating \"%s\"."), canon); krb5_free_principal(context, princ.principal); free(canon); if (ks_tuple != NULL) free(ks_tuple); kadmin_free_tl_data(&princ); return; } if (randkey) { /* more special stuff for -randkey */ if (ks_tuple != NULL || local_kadmin) { retval = kadm5_randkey_principal_3(handle, princ.principal, FALSE, n_ks_tuple, ks_tuple, NULL, NULL); } else { retval = kadm5_randkey_principal(handle, princ.principal, NULL, NULL); } if (retval) { com_err("add_principal", retval, gettext("while randomizing key for \"%s\"."), canon); krb5_free_principal(context, princ.principal); free(canon); if (ks_tuple != NULL) free(ks_tuple); kadmin_free_tl_data(&princ); return; } } /* * We now retrieve the intersection set of the generic flag settings and * the ones specified on the cli & re-parse the princ args, just to make * sure we account for conflicts between 'default_principal_flags' and * the cmdline flag args. While we are here, also clear 'notix'. */ if (randkey || (mask & KADM5_ATTRIBUTES)) { retval = kadm5_get_principal(handle, princ.principal, &dprinc, KADM5_PRINCIPAL_NORMAL_MASK); if (retval == 0) { if (dprinc.attributes != 0) princ.attributes = dprinc.attributes; } else { com_err("add_principal", retval, gettext("while doing a get_principal on \"%s\"."), canon); printf(gettext("\nWarning: Principal \"%s\" could have incomplete " "flag settings, as a result of a failed get_principal.\n" "Check the 'default_principal_flags' setting in kdc.conf(5).\n" "If there is a mismatch, use modprinc in kadmin(8) to rectify " "the same.\n\n"), canon); } /* * Solaris Kerberos: We unset KRB5_KDB_DISALLOW_ALL_TIX before * kadmin_parse_princ_args is called, because -allow_tix may * have been an argument. We still have to unset here because * kadmin_parse_princ_args will not reset the attribute unless * it is was explicity defined. */ princ.attributes &= ~KRB5_KDB_DISALLOW_ALL_TIX; (void) kadmin_parse_princ_args(argc, argv, &princ, &mask, &pass, &randkey, &ks_tuple, &n_ks_tuple, "add_principal"); mask = KADM5_ATTRIBUTES; retval = kadm5_modify_principal(handle, &princ, mask); if (retval) { com_err("add_principal", retval, gettext("while doing a modify_principal to restore flag " "settings for \"%s\"."), canon); krb5_free_principal(context, princ.principal); free(canon); if (ks_tuple != NULL) free(ks_tuple); kadmin_free_tl_data(&princ); return; } } krb5_free_principal(context, princ.principal); printf(gettext("Principal \"%s\" created.\n"), canon); if (ks_tuple != NULL) free(ks_tuple); free(canon); kadmin_free_tl_data(&princ); } void kadmin_modprinc(argc, argv) int argc; char *argv[]; { kadm5_principal_ent_rec princ, oldprinc; krb5_principal kprinc; long mask; krb5_error_code retval; char *pass, *canon; int randkey = 0; int n_ks_tuple = 0; krb5_key_salt_tuple *ks_tuple; if (argc < 2) { kadmin_modprinc_usage("modify_principal"); return; } memset(&oldprinc, 0, sizeof(oldprinc)); memset(&princ, 0, sizeof(princ)); retval = kadmin_parse_name(argv[argc - 1], &kprinc); if (retval) { com_err("modify_principal", retval, gettext("while parsing principal")); return; } retval = krb5_unparse_name(context, kprinc, &canon); if (retval) { com_err("modify_principal", retval, gettext("while canonicalizing principal")); krb5_free_principal(context, kprinc); return; } retval = kadm5_get_principal(handle, kprinc, &oldprinc, KADM5_PRINCIPAL_NORMAL_MASK); krb5_free_principal(context, kprinc); if (retval) { com_err("modify_principal", retval, gettext("while getting \"%s\"."), canon); free(canon); return; } princ.attributes = oldprinc.attributes; kadm5_free_principal_ent(handle, &oldprinc); retval = kadmin_parse_princ_args(argc, argv, &princ, &mask, &pass, &randkey, &ks_tuple, &n_ks_tuple, "modify_principal"); if (ks_tuple != NULL) { free(ks_tuple); kadmin_modprinc_usage("modify_principal"); free(canon); kadmin_free_tl_data(&princ); return; } if (retval) { kadmin_modprinc_usage("modify_principal"); free(canon); kadmin_free_tl_data(&princ); return; } if (randkey) { fprintf(stderr, "modify_principal: -randkey %s ", gettext("not allowed\n")); krb5_free_principal(context, princ.principal); free(canon); kadmin_free_tl_data(&princ); return; } if (pass) { fprintf(stderr, "modify_principal: -pw %s change_password\n", gettext("not allowed; use")); krb5_free_principal(context, princ.principal); free(canon); kadmin_free_tl_data(&princ); return; } retval = kadm5_modify_principal(handle, &princ, mask); krb5_free_principal(context, princ.principal); if (retval) { com_err("modify_principal", retval, gettext("while modifying \"%s\"."), canon); free(canon); kadmin_free_tl_data(&princ); return; } printf(gettext("Principal \"%s\" modified.\n"), canon); kadmin_free_tl_data(&princ); free(canon); } void kadmin_getprinc(argc, argv) int argc; char *argv[]; { kadm5_principal_ent_rec dprinc; krb5_principal princ; krb5_error_code retval; char *canon, *modcanon; int i; if (! (argc == 2 || (argc == 3 && !strcmp("-terse", argv[1])))) { fprintf(stderr, "%s: get_principal [-terse] %s\n", gettext("usage"), gettext("principal")); return; } memset(&dprinc, 0, sizeof(dprinc)); memset(&princ, 0, sizeof(princ)); retval = kadmin_parse_name(argv[argc - 1], &princ); if (retval) { com_err("get_principal", retval, gettext("while parsing principal")); return; } retval = krb5_unparse_name(context, princ, &canon); if (retval) { com_err("get_principal", retval, gettext("while canonicalizing principal")); krb5_free_principal(context, princ); return; } retval = kadm5_get_principal(handle, princ, &dprinc, KADM5_PRINCIPAL_NORMAL_MASK | KADM5_KEY_DATA); krb5_free_principal(context, princ); if (retval) { com_err("get_principal", retval, gettext("while retrieving \"%s\"."), canon); free(canon); return; } retval = krb5_unparse_name(context, dprinc.mod_name, &modcanon); if (retval) { com_err("get_principal", retval, gettext("while unparsing modname")); kadm5_free_principal_ent(handle, &dprinc); free(canon); return; } if (argc == 2) { printf(gettext("Principal: %s\n"), canon); printf(gettext("Expiration date: %s\n"), dprinc.princ_expire_time ? strdate(dprinc.princ_expire_time) : gettext("[never]")); printf(gettext("Last password change: %s\n"), dprinc.last_pwd_change ? strdate(dprinc.last_pwd_change) : gettext("[never]")); printf(gettext("Password expiration date: %s\n"), dprinc.pw_expiration ? strdate(dprinc.pw_expiration) : gettext("[none]")); printf(gettext("Maximum ticket life: %s\n"), strdur(dprinc.max_life)); printf(gettext("Maximum renewable life: %s\n"), strdur(dprinc.max_renewable_life)); printf(gettext("Last modified: %s (%s)\n"), strdate(dprinc.mod_date), modcanon); printf(gettext("Last successful authentication: %s\n"), dprinc.last_success ? strdate(dprinc.last_success) : gettext("[never]")); printf(gettext("Last failed authentication: %s\n"), dprinc.last_failed ? strdate(dprinc.last_failed) : gettext("[never]")); printf(gettext("Failed password attempts: %d\n"), dprinc.fail_auth_count); printf(gettext("Number of keys: %d\n"), dprinc.n_key_data); for (i = 0; i < dprinc.n_key_data; i++) { krb5_key_data *key_data = &dprinc.key_data[i]; char enctype[BUFSIZ], salttype[BUFSIZ]; if (krb5_enctype_to_string(key_data->key_data_type[0], enctype, sizeof(enctype))) snprintf(enctype, sizeof (enctype), gettext(""), key_data->key_data_type[0]); printf("Key: vno %d, %s, ", key_data->key_data_kvno, enctype); if (key_data->key_data_ver > 1) { if (krb5_salttype_to_string(key_data->key_data_type[1], salttype, sizeof(salttype))) snprintf(salttype, sizeof(salttype), gettext(""), key_data->key_data_type[1]); printf("%s\n", salttype); } else printf(gettext("no salt\n")); } printf(gettext("Attributes:")); for (i = 0; i < sizeof (prflags) / sizeof (char *); i++) { if (dprinc.attributes & (krb5_flags) 1 << i) printf(" %s", prflags[i]); } printf("\n"); printf(gettext("Policy: %s\n"), dprinc.policy ? dprinc.policy : gettext("[none]")); } else { printf("\"%s\"\t%d\t%d\t%d\t%d\t\"%s\"\t%d\t%d\t%d\t%d\t\"%s\"" "\t%d\t%d\t%d\t%d\t%d", canon, dprinc.princ_expire_time, dprinc.last_pwd_change, dprinc.pw_expiration, dprinc.max_life, modcanon, dprinc.mod_date, dprinc.attributes, dprinc.kvno, dprinc.mkvno, dprinc.policy ? dprinc.policy : gettext("[none]"), dprinc.max_renewable_life, dprinc.last_success, dprinc.last_failed, dprinc.fail_auth_count, dprinc.n_key_data); for (i = 0; i < dprinc.n_key_data; i++) printf("\t%d\t%d\t%d\t%d", dprinc.key_data[i].key_data_ver, dprinc.key_data[i].key_data_kvno, dprinc.key_data[i].key_data_type[0], dprinc.key_data[i].key_data_type[1]); printf("\n"); } free(modcanon); kadm5_free_principal_ent(handle, &dprinc); free(canon); } void kadmin_getprincs(argc, argv) int argc; char *argv[]; { krb5_error_code retval; char *expr, **names; int i, count; FILE *output; int fd; struct sigaction nsig, osig; sigset_t nmask, omask; int waitb; expr = NULL; if (! (argc == 1 || (argc == 2 && (expr = argv[1])))) { fprintf(stderr, "%s: get_principals %s\n", gettext("usage"), gettext("[expression]")); return; } retval = kadm5_get_principals(handle, expr, &names, &count); if (retval) { com_err("get_principals", retval, gettext("while retrieving list.")); return; } /* * Solaris: the following code is used for paging */ sigemptyset(&nmask); sigaddset(&nmask, SIGINT); sigprocmask(SIG_BLOCK, &nmask, &omask); nsig.sa_handler = SIG_IGN; sigemptyset(&nsig.sa_mask); nsig.sa_flags = 0; sigaction(SIGINT, &nsig, &osig); fd = ss_pager_create(); output = fdopen(fd, "w"); sigprocmask(SIG_SETMASK, &omask, (sigset_t *)0); for (i = 0; i < count; i++) fprintf(output, "%s\n", names[i]); fclose(output); wait(&waitb); /* Solaris Kerberos: * Restore the original handler for SIGINT */ if (sigaction(SIGINT, &osig, (struct sigaction *)0) == -1) { perror("sigaction"); } kadm5_free_name_list(handle, names, count); } static int kadmin_parse_policy_args(argc, argv, policy, mask, caller) int argc; char *argv[]; kadm5_policy_ent_t policy; long *mask; char *caller; { int i; time_t now; time_t date; time(&now); *mask = 0; for (i = 1; i < argc - 1; i++) { if (strlen(argv[i]) == 8 && !strcmp(argv[i], "-maxlife")) { if (++i > argc -2) return -1; else { date = get_date(argv[i]); if (date == (time_t)-1) { fprintf(stderr, gettext("Invalid date specification \"%s\".\n"), argv[i]); return -1; } policy->pw_max_life = date - now; *mask |= KADM5_PW_MAX_LIFE; continue; } } else if (strlen(argv[i]) == 8 && !strcmp(argv[i], "-minlife")) { if (++i > argc - 2) return -1; else { date = get_date(argv[i]); if (date == (time_t)-1) { fprintf(stderr, gettext("Invalid date specification \"%s\".\n"), argv[i]); return -1; } policy->pw_min_life = date - now; *mask |= KADM5_PW_MIN_LIFE; continue; } } else if (strlen(argv[i]) == 10 && !strcmp(argv[i], "-minlength")) { if (++i > argc - 2) return -1; else { policy->pw_min_length = atoi(argv[i]); *mask |= KADM5_PW_MIN_LENGTH; continue; } } else if (strlen(argv[i]) == 11 && !strcmp(argv[i], "-minclasses")) { if (++i > argc - 2) return -1; else { policy->pw_min_classes = atoi(argv[i]); *mask |= KADM5_PW_MIN_CLASSES; continue; } } else if (strlen(argv[i]) == 8 && !strcmp(argv[i], "-history")) { if (++i > argc - 2) return -1; else { policy->pw_history_num = atoi(argv[i]); *mask |= KADM5_PW_HISTORY_NUM; continue; } } else return -1; } if (i != argc -1) { fprintf(stderr, gettext("%s: parser lost count!\n"), caller); return -1; } else return 0; } static void kadmin_addmodpol_usage(func) char *func; { fprintf(stderr, "%s: %s %s\n", gettext("usage"), func, gettext("[options] policy")); fprintf(stderr, gettext("\toptions are:\n")); fprintf(stderr, "\t\t[-maxlife time] [-minlife time] " "[-minlength length]\n\t\t[-minclasses number] " "[-history number]\n"); } void kadmin_addpol(argc, argv) int argc; char *argv[]; { krb5_error_code retval; long mask; kadm5_policy_ent_rec policy; memset(&policy, 0, sizeof(policy)); if (kadmin_parse_policy_args(argc, argv, &policy, &mask, "add_policy")) { kadmin_addmodpol_usage("add_policy"); return; } else { policy.policy = argv[argc - 1]; mask |= KADM5_POLICY; retval = kadm5_create_policy(handle, &policy, mask); if (retval) { com_err("add_policy", retval, gettext("while creating policy \"%s\"."), policy.policy); return; } } return; } void kadmin_modpol(argc, argv) int argc; char *argv[]; { krb5_error_code retval; long mask; kadm5_policy_ent_rec policy; memset(&policy, 0, sizeof(policy)); if (kadmin_parse_policy_args(argc, argv, &policy, &mask, "modify_policy")) { kadmin_addmodpol_usage("modify_policy"); return; } else { policy.policy = argv[argc - 1]; retval = kadm5_modify_policy(handle, &policy, mask); if (retval) { com_err("modify_policy", retval, gettext("while modifying policy \"%s\"."), policy.policy); return; } } return; } void kadmin_delpol(argc, argv) int argc; char *argv[]; { krb5_error_code retval; char reply[32]; if (! (argc == 2 || (argc == 3 && !strcmp("-force", argv[1])))) { fprintf(stderr, "%s: delete_policy [-force] %s\n", gettext("usage"), gettext("policy")); return; } if (argc == 2) { printf(gettext("Are you sure you want to delete the policy " "\"%s\"? (yes/no): "), argv[1]); fgets(reply, sizeof (reply), stdin); if (strncmp(gettext("yes\n"), reply, sizeof (reply)) && strncmp(gettext("y\n"), reply, sizeof (reply)) && strncmp(gettext("Y\n"), reply, sizeof (reply)) ) { fprintf(stderr, gettext("Policy \"%s\" not deleted.\n"), argv[1]); return; } } retval = kadm5_delete_policy(handle, argv[argc - 1]); if (retval) { com_err("delete_policy:", retval, gettext("while deleting policy \"%s\""), argv[argc - 1]); return; } return; } void kadmin_getpol(argc, argv) int argc; char *argv[]; { krb5_error_code retval; kadm5_policy_ent_rec policy; if (! (argc == 2 || (argc == 3 && !strcmp("-terse", argv[1])))) { fprintf(stderr, "%s: get_policy [-terse] %s\n", gettext("usage"), gettext("policy")); return; } retval = kadm5_get_policy(handle, argv[argc - 1], &policy); if (retval) { com_err("get_policy", retval, gettext("while retrieving policy \"%s\"."), argv[argc - 1]); return; } if (argc == 2) { printf(gettext("Policy: %s\n"), policy.policy); printf(gettext("Maximum password life: %ld\n"), policy.pw_max_life); printf(gettext("Minimum password life: %ld\n"), policy.pw_min_life); printf(gettext("Minimum password length: %ld\n"), policy.pw_min_length); printf(gettext("Minimum number of password " "character classes: %ld\n"), policy.pw_min_classes); printf(gettext("Number of old keys kept: %ld\n"), policy.pw_history_num); printf(gettext("Reference count: %ld\n"), policy.policy_refcnt); } else { printf("\"%s\"\t%ld\t%ld\t%ld\t%ld\t%ld\t%ld\n", policy.policy, policy.pw_max_life, policy.pw_min_life, policy.pw_min_length, policy.pw_min_classes, policy.pw_history_num, policy.policy_refcnt); } kadm5_free_policy_ent(handle, &policy); return; } void kadmin_getpols(argc, argv) int argc; char *argv[]; { krb5_error_code retval; char *expr, **names; int i, count; /* Solaris Kerberos: * Use a pager for listing policies (similar to listing princs) */ FILE *output = NULL; int fd; struct sigaction nsig, osig; sigset_t nmask, omask; int waitb; expr = NULL; if (! (argc == 1 || (argc == 2 && (expr = argv[1])))) { fprintf(stderr, "%s: get_policies %s\n", gettext("usage"), gettext("[expression]\n")); return; } retval = kadm5_get_policies(handle, expr, &names, &count); if (retval) { com_err("get_policies", retval, gettext("while retrieving list.")); return; } if (sigemptyset(&nmask) == -1) { perror("sigemptyset"); kadm5_free_name_list(handle, names, count); return; } if (sigaddset(&nmask, SIGINT) == -1) { perror("sigaddset"); kadm5_free_name_list(handle, names, count); return; } if (sigemptyset(&nsig.sa_mask) == -1) { perror("sigemptyset"); kadm5_free_name_list(handle, names, count); return; } if (sigprocmask(SIG_BLOCK, &nmask, &omask) == -1) { perror("sigprocmask"); kadm5_free_name_list(handle, names, count); return; } nsig.sa_handler = SIG_IGN; nsig.sa_flags = 0; if (sigaction(SIGINT, &nsig, &osig) == -1) { perror("sigaction"); if (sigprocmask(SIG_SETMASK, &omask, (sigset_t *)0) == -1) { perror("sigprocmask"); } kadm5_free_name_list(handle, names, count); return; } fd = ss_pager_create(); if (fd == -1) { fprintf(stderr, "%s: failed to create pager\n", whoami); if (sigprocmask(SIG_SETMASK, &omask, (sigset_t *)0) == -1) { perror("sigprocmask"); } if (sigaction(SIGINT, &osig, (struct sigaction *)0) == -1) { perror("sigaction"); } kadm5_free_name_list(handle, names, count); return; } output = fdopen(fd, "w"); if (output == NULL) { perror("fdopen"); } if (sigprocmask(SIG_SETMASK, &omask, (sigset_t *)0) == -1) { perror("sigprocmask"); } if (output != NULL) { for (i = 0; i < count; i++) fprintf(output, "%s\n", names[i]); } if (output != NULL && fclose(output) != 0) { perror("fclose"); } if (wait(&waitb) == -1) { perror("wait"); } if (sigaction(SIGINT, &osig, (struct sigaction *)0) == -1) { perror("sigaction"); } kadm5_free_name_list(handle, names, count); } /* * kadmin/cli/kadmin.h * * Copyright 2001 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. * * * Prototypes for kadmin functions called from SS library. */ #ifndef __KADMIN_H__ #define __KADMIN_H__ /* It would be nice if ss produced a header file we could reference */ extern char *kadmin_startup(int argc, char *argv[]); extern int quit (void); extern void kadmin_lock(int argc, char *argv[]); extern void kadmin_unlock(int argc, char *argv[]); extern void kadmin_delprinc(int argc, char *argv[]); extern void kadmin_cpw(int argc, char *argv[]); extern void kadmin_addprinc(int argc, char *argv[]); extern void kadmin_modprinc(int argc, char *argv[]); extern void kadmin_getprinc(int argc, char *argv[]); extern void kadmin_getprincs(int argc, char *argv[]); extern void kadmin_addpol(int argc, char *argv[]); extern void kadmin_modpol(int argc, char *argv[]); extern void kadmin_delpol(int argc, char *argv[]); extern void kadmin_getpol(int argc, char *argv[]); extern void kadmin_getpols(int argc, char *argv[]); extern void kadmin_getprivs(int argc, char *argv[]); extern void kadmin_keytab_add(int argc, char *argv[]); extern void kadmin_keytab_remove(int argc, char *argv[]); #ifdef TIME_WITH_SYS_TIME #include #include #else #ifdef HAVE_SYS_TIME_H #include #else #include #endif #endif extern time_t get_date(char *); /* Yucky global variables */ extern krb5_context context; extern char *krb5_defkeyname; extern char *whoami; extern void *handle; #endif /* __KADMIN_H__ */ /* * 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_ct.c - automatically generated from kadmin_ct.ct */ /* Above no longer appears to be true */ /* * 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 #include #ifndef __STDC__ #define const #endif static char const * const ssu00001[] = { "add_principal", "addprinc", "ank", (char const *)0 }; extern void kadmin_addprinc __SS_PROTO; static char const * const ssu00002[] = { "delete_principal", "delprinc", (char const *)0 }; extern void kadmin_delprinc __SS_PROTO; static char const * const ssu00003[] = { "modify_principal", "modprinc", (char const *)0 }; extern void kadmin_modprinc __SS_PROTO; static char const * const ssu00004[] = { "change_password", "cpw", (char const *)0 }; extern void kadmin_cpw __SS_PROTO; static char const * const ssu00005[] = { "get_principal", "getprinc", (char const *)0 }; extern void kadmin_getprinc __SS_PROTO; static char const * const ssu00006[] = { "list_principals", "listprincs", "get_principals", "getprincs", (char const *)0 }; extern void kadmin_getprincs __SS_PROTO; static char const * const ssu00007[] = { "add_policy", "addpol", (char const *)0 }; extern void kadmin_addpol __SS_PROTO; static char const * const ssu00008[] = { "modify_policy", "modpol", (char const *)0 }; extern void kadmin_modpol __SS_PROTO; static char const * const ssu00009[] = { "delete_policy", "delpol", (char const *)0 }; extern void kadmin_delpol __SS_PROTO; static char const * const ssu00010[] = { "get_policy", "getpol", (char const *)0 }; extern void kadmin_getpol __SS_PROTO; static char const * const ssu00011[] = { "list_policies", "listpols", "get_policies", "getpols", (char const *)0 }; extern void kadmin_getpols __SS_PROTO; static char const * const ssu00012[] = { "get_privs", "getprivs", (char const *)0 }; extern void kadmin_getprivs __SS_PROTO; static char const * const ssu00013[] = { "ktadd", "xst", (char const *)0 }; extern void kadmin_keytab_add __SS_PROTO; static char const * const ssu00014[] = { "ktremove", "ktrem", (char const *)0 }; extern void kadmin_keytab_remove __SS_PROTO; static char const * const ssu00015[] = { "lock", (char const *)0 }; extern void kadmin_lock __SS_PROTO; static char const * const ssu00016[] = { "unlock", (char const *)0 }; extern void kadmin_unlock __SS_PROTO; static char const * const ssu00017[] = { "list_requests", "lr", "?", (char const *)0 }; extern void ss_list_requests __SS_PROTO; static char const * const ssu00018[] = { "quit", "exit", "q", (char const *)0 }; extern void ss_quit __SS_PROTO; static ss_request_entry ssu00019[] = { { ssu00001, kadmin_addprinc, gettext("Add principal"), 0 }, { ssu00002, kadmin_delprinc, gettext("Delete principal"), 0 }, { ssu00003, kadmin_modprinc, gettext("Modify principal"), 0 }, { ssu00004, kadmin_cpw, gettext("Change password"), 0 }, { ssu00005, kadmin_getprinc, gettext("Get principal"), 0 }, { ssu00006, kadmin_getprincs, gettext("List principals"), 0 }, { ssu00007, kadmin_addpol, gettext("Add policy"), 0 }, { ssu00008, kadmin_modpol, gettext("Modify policy"), 0 }, { ssu00009, kadmin_delpol, gettext("Delete policy"), 0 }, { ssu00010, kadmin_getpol, gettext("Get policy"), 0 }, { ssu00011, kadmin_getpols, gettext("List policies"), 0 }, { ssu00012, kadmin_getprivs, gettext("Get privileges"), 0 }, { ssu00013, kadmin_keytab_add, gettext("Add entry(s) to a keytab"), 0 }, { ssu00014, kadmin_keytab_remove, gettext("Remove entry(s) from a keytab"), 0 }, { ssu00015, kadmin_lock, gettext("Lock database exclusively (use with extreme caution!)"), 0 }, { ssu00016, kadmin_unlock, gettext("Release exclusive database lock"), 0 }, { ssu00017, ss_list_requests, gettext("List available requests."), 0 }, { ssu00018, ss_quit, gettext("Exit program."), 0 }, { 0, 0, 0, 0 } }; ss_request_table kadmin_cmds = { 2, ssu00019 }; #undef gettext /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Contains kadmin.local specific code. */ #include #include #include #include #include void usage(char *whoami) { fprintf(stderr, "%s: %s [-r realm] [-p principal] [-q query] " "[-d dbname] [-e \"enc:salt ...\"] [-m] [-D]\n", gettext("Usage"), whoami); exit(1); } /* * Debugging function * Turns on low level debugging in db module * Requires that db library be compiled with -DDEBUG_DB flag */ /* ARGSUSED */ void debugEnable(int displayMsgs) { #if DEBUG_DB debugDisplayDB(displayMsgs); #endif #if DEBUG /* Solaris Kerberos: not supported */ /* debugDisplaySS(displayMsgs); */ #endif } void kadmin_getprivs(argc, argv) int argc; char *argv[]; { static char *privs[] = {"GET", "ADD", "MODIFY", "DELETE", "LIST", "CHANGEPW"}; krb5_error_code retval; int i; long plist; /* for kadmin.local return all privilages */ printf(gettext("current privileges:")); for (i = 0; i < sizeof (privs) / 4; i++) { printf(" %s", gettext(privs[i])); } printf("\n"); } /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Contains remote client specific code. */ #include #include #include #include #include extern void *handle; void usage(char *whoami) { fprintf(stderr, "%s: %s [-r realm] [-p principal] [-q query] " "[-s admin_server[:port]] [[-c ccache]|[-k [-t keytab]]" "|[-w password]]\n", gettext("Usage"), whoami); exit(1); } /* * Debugging function - for remote admin client */ /* ARGSUSED */ void debugEnable(int displayMsgs) { #ifdef DEBUG /* Solaris Kerberos: not supported */ /* debugDisplaySS(displayMsgs); */ #endif } void kadmin_getprivs(argc, argv) int argc; char *argv[]; { static char *privs[] = {"GET", "ADD", "MODIFY", "DELETE", "LIST", "CHANGE"}; krb5_error_code retval; int i; long plist; if (argc != 1) { fprintf(stderr, "%s: get_privs\n", gettext("usage")); return; } retval = kadm5_get_privs(handle, &plist); if (retval) { com_err("get_privs", retval, gettext("while retrieving privileges")); return; } printf(gettext("current privileges:")); for (i = 0; i < sizeof (privs) / sizeof (char *); i++) { if (plist & 1 << i) printf(" %s", gettext(privs[i])); } printf("\n"); } /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. * * $Id: keytab.c,v 1.28 2004/05/31 12:39:16 epeisach Exp $ * $Source: /cvs/krbdev/krb5/src/kadmin/cli/keytab.c,v $ */ /* * Copyright (C) 1998 by the FundsXpress, INC. * * 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 FundsXpress. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. FundsXpress makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #if !defined(lint) && !defined(__CODECENTER__) static char *rcsid = "$Header: /cvs/krbdev/krb5/src/kadmin/cli/keytab.c,v 1.28 2004/05/31 12:39:16 epeisach Exp $"; #endif #include #include #include #include #include #include #include "kadmin.h" #include static int add_principal(void *lhandle, char *keytab_str, krb5_keytab keytab, krb5_boolean keepold, int n_ks_tuple, krb5_key_salt_tuple *ks_tuple, char *princ_str); static int remove_principal(char *keytab_str, krb5_keytab keytab, char *princ_str, char *kvno_str); static char *etype_string(krb5_enctype enctype); static char *etype_istring(krb5_enctype enctype); static int quiet; static void add_usage() { fprintf(stderr, "%s: %s\n", gettext("Usage"), "ktadd [-k[eytab] keytab] [-q] [-e keysaltlist] " "[principal | -glob princ-exp] [...]\n"); } static void rem_usage() { fprintf(stderr, "%s: %s\n", gettext("Usage"), "ktremove [-k[eytab] keytab] [-q] principal " "[kvno|\"all\"|\"old\"]\n"); } static int process_keytab(krb5_context my_context, char **keytab_str, krb5_keytab *keytab) { int code; char buf[BUFSIZ]; if (*keytab_str == NULL) { if (code = krb5_kt_default(my_context, keytab)) { com_err(whoami, code, gettext("while opening default keytab")); return 1; } if (code = krb5_kt_get_name(my_context, *keytab, buf, BUFSIZ)) { com_err(whoami, code, gettext("while retrieving keytab name")); return 1; } if (!(*keytab_str = strdup(buf))) { com_err(whoami, ENOMEM, gettext("while creating keytab name")); return 1; } } else { if (strchr(*keytab_str, ':') != NULL) { *keytab_str = strdup(*keytab_str); if (*keytab_str == NULL) { com_err(whoami, ENOMEM, gettext("while creating keytab name")); return 1; } } else { char *tmp = *keytab_str; *keytab_str = (char *) malloc(strlen("WRFILE:")+strlen(tmp)+1); if (*keytab_str == NULL) { com_err(whoami, ENOMEM, gettext("while creating keytab name")); return 1; } sprintf(*keytab_str, "WRFILE:%s", tmp); } code = krb5_kt_resolve(my_context, *keytab_str, keytab); if (code != 0) { com_err(whoami, code, gettext("while resolving keytab %s"), *keytab_str); free(keytab_str); return 1; } } return 0; } void kadmin_keytab_add(int argc, char **argv) { krb5_keytab keytab = 0; char *keytab_str = NULL, **princs; int code, num, i; krb5_error_code retval; int n_ks_tuple = 0; krb5_boolean keepold = FALSE; krb5_key_salt_tuple *ks_tuple = NULL; argc--; argv++; quiet = 0; while (argc) { if (strncmp(*argv, "-k", 2) == 0) { argc--; argv++; if (!argc || keytab_str) { add_usage(); return; } keytab_str = *argv; } else if (strcmp(*argv, "-q") == 0) { quiet++; } else if (strcmp(*argv, "-e") == 0) { argc--; if (argc < 1) { add_usage(); return; } retval = krb5_string_to_keysalts(*++argv, ", \t", ":.-", 0, &ks_tuple, &n_ks_tuple); if (retval) { com_err("ktadd", retval, gettext("while parsing keysalts %s"), *argv); return; } } else break; argc--; argv++; } if (argc == 0) { add_usage(); return; } if (process_keytab(context, &keytab_str, &keytab)) return; while (*argv) { if (strcmp(*argv, "-glob") == 0) { if (*++argv == NULL) { add_usage(); break; } code = kadm5_get_principals(handle, *argv, &princs, &num); if (code) { com_err(whoami, code, gettext("while expanding expression " "\"%s\"."), *argv); argv++; continue; } for (i = 0; i < num; i++) (void) add_principal(handle, keytab_str, keytab, keepold, n_ks_tuple, ks_tuple, princs[i]); kadm5_free_name_list(handle, princs, num); } else (void) add_principal(handle, keytab_str, keytab, keepold, n_ks_tuple, ks_tuple, *argv); argv++; } code = krb5_kt_close(context, keytab); if (code != 0) com_err(whoami, code, gettext("while closing keytab")); free(keytab_str); } void kadmin_keytab_remove(int argc, char **argv) { krb5_keytab keytab = 0; char *keytab_str = NULL; int code; argc--; argv++; quiet = 0; while (argc) { if (strncmp(*argv, "-k", 2) == 0) { argc--; argv++; if (!argc || keytab_str) { rem_usage(); return; } keytab_str = *argv; } else if (strcmp(*argv, "-q") == 0) { quiet++; } else break; argc--; argv++; } if (argc != 1 && argc != 2) { rem_usage(); return; } if (process_keytab(context, &keytab_str, &keytab)) return; (void) remove_principal(keytab_str, keytab, argv[0], argv[1]); code = krb5_kt_close(context, keytab); if (code != 0) com_err(whoami, code, gettext("while closing keytab")); free(keytab_str); } static int add_principal(void *lhandle, char *keytab_str, krb5_keytab keytab, krb5_boolean keepold, int n_ks_tuple, krb5_key_salt_tuple *ks_tuple, char *princ_str) { kadm5_principal_ent_rec princ_rec; krb5_principal princ; krb5_keytab_entry new_entry; krb5_keyblock *keys; int code, nkeys, i; int nktypes = 0; krb5_key_salt_tuple *permitted_etypes = NULL; (void) memset((char *)&princ_rec, 0, sizeof(princ_rec)); princ = NULL; keys = NULL; nkeys = 0; code = krb5_parse_name(context, princ_str, &princ); if (code != 0) { com_err(whoami, code, gettext("while parsing -add principal name %s"), princ_str); goto cleanup; } if (ks_tuple == NULL) { krb5_enctype *ptr, *ktypes = NULL; code = krb5_get_permitted_enctypes(context, &ktypes); if (!code && ktypes && *ktypes) { krb5_int32 salttype; /* * Count the results. This is stupid, the API above * should have included an output param to indicate * the size of the list that is returned. */ for (ptr = ktypes; *ptr; ptr++) nktypes++; /* Allocate a new key-salt tuple set */ permitted_etypes = (krb5_key_salt_tuple *)malloc ( sizeof (krb5_key_salt_tuple) * nktypes); if (permitted_etypes == NULL) { free(ktypes); return (ENOMEM); } /* * Because the keysalt parameter doesn't matter for * keys stored in the keytab, use the default "normal" * salt for all keys */ (void) krb5_string_to_salttype("normal", &salttype); for (i = 0; i < nktypes; i++) { permitted_etypes[i].ks_enctype = ktypes[i]; permitted_etypes[i].ks_salttype = salttype; } free(ktypes); } else { if (ktypes) free(ktypes); goto cleanup; } } else { permitted_etypes = ks_tuple; nktypes = n_ks_tuple; } code = kadm5_randkey_principal_3(lhandle, princ, keepold, nktypes, permitted_etypes, &keys, &nkeys); #ifndef _KADMIN_LOCAL_ /* this block is not needed in the kadmin.local client */ /* * If the above call failed, we may be talking to an older * admin server, so try the older API. */ if (code == KADM5_RPC_ERROR) { code = kadm5_randkey_principal_old(handle, princ, &keys, &nkeys); } #endif /* !KADMIN_LOCAL */ if (code != 0) { if (code == KADM5_UNK_PRINC) { fprintf(stderr, gettext("%s: Principal %s does not exist.\n"), whoami, princ_str); /* Solaris Kerberos: Better error messages */ } else if (code == KRB5_BAD_ENCTYPE) { int i, et; fprintf(stderr, gettext("%s: Error from the remote system: " "%s while changing %s's key\n"), whoami, error_message(code), princ_str); if (nktypes) { et = permitted_etypes[0].ks_enctype; fprintf(stderr, gettext("%s: Encryption types " "requested: %s (%d)"), whoami, etype_istring(et), et); for (i = 1; i < nktypes; i++) { et = permitted_etypes[i].ks_enctype; fprintf(stderr, ", %s (%d)", etype_istring(et), et); } fprintf(stderr, "\n"); } } else { com_err(whoami, code, gettext("while changing %s's key"), princ_str); } goto cleanup; } code = kadm5_get_principal(lhandle, princ, &princ_rec, KADM5_PRINCIPAL_NORMAL_MASK); if (code != 0) { com_err(whoami, code, gettext("while retrieving principal")); goto cleanup; } for (i = 0; i < nkeys; i++) { memset((char *) &new_entry, 0, sizeof(new_entry)); new_entry.principal = princ; new_entry.key = keys[i]; new_entry.vno = princ_rec.kvno; code = krb5_kt_add_entry(context, keytab, &new_entry); if (code != 0) { com_err(whoami, code, gettext("while adding key to keytab")); (void) kadm5_free_principal_ent(lhandle, &princ_rec); goto cleanup; } if (!quiet) printf(gettext("Entry for principal %s with kvno %d, " "encryption type %s added to keytab %s.\n"), princ_str, princ_rec.kvno, etype_string(keys[i].enctype), keytab_str); } code = kadm5_free_principal_ent(lhandle, &princ_rec); if (code != 0) { com_err(whoami, code, gettext("while freeing principal entry")); goto cleanup; } cleanup: if (nkeys) { for (i = 0; i < nkeys; i++) krb5_free_keyblock_contents(context, &keys[i]); free(keys); } if (princ) krb5_free_principal(context, princ); if (permitted_etypes != NULL && ks_tuple == NULL) free(permitted_etypes); return code; } int remove_principal(char *keytab_str, krb5_keytab keytab, char *princ_str, char *kvno_str) { krb5_principal princ; krb5_keytab_entry entry; krb5_kt_cursor cursor; enum { UNDEF, SPEC, HIGH, ALL, OLD } mode; int code, did_something; krb5_kvno kvno; code = krb5_parse_name(context, princ_str, &princ); if (code != 0) { com_err(whoami, code, gettext("while parsing principal name %s"), princ_str); return code; } mode = UNDEF; if (kvno_str == NULL) { mode = HIGH; kvno = 0; } else if (strcmp(kvno_str, "all") == 0) { mode = ALL; kvno = 0; } else if (strcmp(kvno_str, "old") == 0) { mode = OLD; kvno = 0; } else { mode = SPEC; kvno = atoi(kvno_str); } /* kvno is set to specified value for SPEC, 0 otherwise */ code = krb5_kt_get_entry(context, keytab, princ, kvno, 0, &entry); if (code != 0) { if (code == ENOENT) { fprintf(stderr, gettext("%s: Keytab %s does not exist.\n"), whoami, keytab_str); } else if (code == KRB5_KT_NOTFOUND) { if (mode != SPEC) fprintf(stderr, gettext("%s: No entry for principal " "%s exists in keytab %s\n"), whoami, princ_str, keytab_str); else fprintf(stderr, gettext("%s: No entry for principal " "%s with kvno %d exists in " "keytab %s.\n"), whoami, princ_str, kvno, keytab_str); } else { com_err(whoami, code, gettext("while retrieving highest " "kvno from keytab")); } return code; } /* set kvno to spec'ed value for SPEC, highest kvno otherwise */ kvno = entry.vno; krb5_kt_free_entry(context, &entry); code = krb5_kt_start_seq_get(context, keytab, &cursor); if (code != 0) { com_err(whoami, code, gettext("while starting keytab scan")); return code; } did_something = 0; while ((code = krb5_kt_next_entry(context, keytab, &entry, &cursor)) == 0) { if (krb5_principal_compare(context, princ, entry.principal) && ((mode == ALL) || (mode == SPEC && entry.vno == kvno) || (mode == OLD && entry.vno != kvno) || (mode == HIGH && entry.vno == kvno))) { /* * Ack! What a kludge... the scanning functions lock * the keytab so entries cannot be removed while they * are operating. */ code = krb5_kt_end_seq_get(context, keytab, &cursor); if (code != 0) { com_err(whoami, code, gettext("while temporarily " "ending keytab scan")); return code; } code = krb5_kt_remove_entry(context, keytab, &entry); if (code != 0) { com_err(whoami, code, gettext("while deleting entry " "from keytab")); return code; } code = krb5_kt_start_seq_get(context, keytab, &cursor); if (code != 0) { com_err(whoami, code, gettext("while restarting keytab scan")); return code; } did_something++; if (!quiet) printf(gettext("Entry for principal " "%s with kvno %d " "removed from keytab %s.\n"), princ_str, entry.vno, keytab_str); } krb5_kt_free_entry(context, &entry); } if (code && code != KRB5_KT_END) { com_err(whoami, code, gettext("while scanning keytab")); return code; } if ((code = krb5_kt_end_seq_get(context, keytab, &cursor))) { com_err(whoami, code, gettext("while ending keytab scan")); return code; } /* * If !did_someting then mode must be OLD or we would have * already returned with an error. But check it anyway just to * prevent unexpected error messages... */ if (!did_something && mode == OLD) { fprintf(stderr, gettext("%s: There is only one entry for principal " "%s in keytab %s\n"), whoami, princ_str, keytab_str); return 1; } return 0; } /* * etype_string(enctype): return a string representation of the * encryption type. XXX copied from klist.c; this should be a * library function, or perhaps just #defines */ static char *etype_string(enctype) krb5_enctype enctype; { static char buf[100]; krb5_error_code ret; if ((ret = krb5_enctype_to_string(enctype, buf, sizeof(buf)))) sprintf(buf, "etype %d", enctype); return buf; } /* Solaris Kerberos */ static char *etype_istring(krb5_enctype enctype) { static char buf[100]; krb5_error_code ret; if ((ret = krb5_enctype_to_istring(enctype, buf, sizeof(buf)))) sprintf(buf, "unknown", enctype); return (buf); } /* * 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 * */ /* * Copyright 1994 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 wrapper for kadmin */ #include #include #include #include #include #include #include "kadmin.h" extern ss_request_table kadmin_cmds; extern int exit_status; extern char *whoami; int main(argc, argv) int argc; char *argv[]; { char *request; krb5_error_code retval; int sci_idx, code = 0; whoami = ((whoami = strrchr(argv[0], '/')) ? whoami+1 : argv[0]); (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); request = kadmin_startup(argc, argv); sci_idx = ss_create_invocation(whoami, "5.0", (char *) NULL, &kadmin_cmds, &retval); if (retval) { ss_perror(sci_idx, retval, gettext("creating invocation")); exit(1); } (void) setlocale(LC_ALL, ""); (void) textdomain(TEXT_DOMAIN); if (request) { code = ss_execute_line(sci_idx, request); if (code != 0) { ss_perror(sci_idx, code, request); exit_status++; } } else retval = ss_listen(sci_idx); return quit() ? 1 : exit_status; } # # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Copyright (c) 2018, Joyent, Inc. PROG= kdb5_util OBJS = kdb5_util.o \ kdb5_create.o kadm5_create.o string_table.o kdb5_stash.o \ kdb5_destroy.o ovload.o strtok.o dump.o SRCS = $(OBJS:.o=.c) CLOBBERFILES += $(TESTPROG) ISRCHDR= iprop.h KRB5IPROPDIR= $(SRC)/cmd/krb5/iprop include ../../../Makefile.cmd include $(SRC)/lib/gss_mechs/mech_krb5/Makefile.mech_krb5 POFILE = $(PROG).po POFILES = generic.po CPPFLAGS += -I. -I$(SRC)/lib/gss_mechs/mech_krb5/include/kerberosIV \ -I$(SRC)/lib/gss_mechs/mech_krb5/include \ -I$(SRC)/lib/gss_mechs/mech_krb5/include/krb5 \ -I$(SRC)/lib/krb5 \ -I$(SRC)/uts/common/gssapi/include \ -I$(SRC)/uts/common/gssapi/mechs/krb5/include \ -I$(KRB5IPROPDIR) \ -DHAVE_LIBSOCKET=1 -DHAVE_LIBNSL=1 -DHAVE_GETCWD=1 \ -DHAVE_STRSTR=1 -DHAVE_COMPILE=1 -DHAVE_STEP=1 \ -DHAVE_KRB_DB_H=1 -DHAVE_KDC_H=1 -DKDB4_DISABLE \ -DUSE_KADM5_API_VERSION=2 CERRWARN += -Wno-parentheses CERRWARN += -Wno-unused-variable CERRWARN += -Wno-unused-function CERRWARN += -Wno-implicit-function-declaration # Hammerhead: Suppress pointer/int cast warnings in legacy code CERRWARN += -Wno-pointer-to-int-cast CERRWARN += -Wno-int-to-pointer-cast # not linted SMATCH=off LDFLAGS += $(KRUNPATH) $(KERBRUNPATH) # Hammerhead: GNU ld needs rpath-link for transitive deps (libdyn, libkdb) via libkadm5srv LDFLAGS += -Wl,-rpath-link,$(ROOT)/usr/lib/krb5 LDLIBS += -L $(ROOT_KLIBDIR) -L $(KRB5LIB) -lkadm5srv -lkdb \ -lmech_krb5 -lnsl .KEEP_STATE: all: $(PROG) # Hammerhead: pre-generated (rpcgen + GNU cpp truncation bug) # iprop.h is now a committed source file. # Explicitly state the dependency on iprop.h $(OBJS): $(ISRCHDR) $(PROG): $(OBJS) $(LINK.c) $(OBJS) -o $@ $(LDLIBS) $(POST_PROCESS) install: $(KRB5SBINPROG) clean: $(RM) $(OBJS) @# Hammerhead: do not delete pre-generated $(ISRCHDR) include ../../../Makefile.targ $(POFILE): $(DERIVED_FILES) .WAIT $(POFILES) $(RM) $@ $(CAT) $(POFILES) > $@ generic.po: FRC $(RM) messages.po $(XGETTEXT) $(XGETFLAGS) `$(GREP) -l gettext *.[ch]` $(SED) "/^domain/d" messages.po > $@ $(RM) messages.po FRC: /* * Copyright 2008 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 * */ /* * admin/edit/dump.c * * Copyright 1990,1991 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. * * * Dump a KDC database */ #include #include #include #include #include #include #include #include "kdb5_util.h" #if defined(HAVE_REGEX_H) && defined(HAVE_REGCOMP) #include #endif /* HAVE_REGEX_H */ /* * Needed for master key conversion. */ extern krb5_keyblock master_key; extern krb5_principal master_princ; static int mkey_convert; static krb5_keyblock new_master_key; static int backwards; static int recursive; /* * Use compile(3) if no regcomp present. */ #if !defined(HAVE_REGCOMP) && defined(HAVE_REGEXP_H) #define INIT char *sp = instring; #define GETC() (*sp++) #define PEEKC() (*sp) #define UNGETC(c) (--sp) #define RETURN(c) return(c) #define ERROR(c) #define RE_BUF_SIZE 1024 #include #endif /* !HAVE_REGCOMP && HAVE_REGEXP_H */ struct dump_args { char *programname; FILE *ofile; krb5_context kcontext; char **names; int nnames; int verbose; }; static krb5_error_code dump_k5beta_iterator (krb5_pointer, krb5_db_entry *); static krb5_error_code dump_k5beta6_iterator (krb5_pointer, krb5_db_entry *); static krb5_error_code dump_k5beta6_iterator_ext (krb5_pointer, krb5_db_entry *, int); static krb5_error_code dump_iprop_iterator (krb5_pointer, krb5_db_entry *); static krb5_error_code dump_k5beta7_princ (krb5_pointer, krb5_db_entry *); static krb5_error_code dump_k5beta7_princ_ext (krb5_pointer, krb5_db_entry *, int); static krb5_error_code dump_k5beta7_princ_withpolicy (krb5_pointer, krb5_db_entry *); static krb5_error_code dump_iprop_princ (krb5_pointer, krb5_db_entry *); static krb5_error_code dump_ov_princ (krb5_pointer, krb5_db_entry *); static void dump_k5beta7_policy (void *, osa_policy_ent_t); typedef krb5_error_code (*dump_func)(krb5_pointer, krb5_db_entry *); static int process_k5beta_record (char *, krb5_context, FILE *, int, int *); static int process_k5beta6_record (char *, krb5_context, FILE *, int, int *); static int process_k5beta7_record (char *, krb5_context, FILE *, int, int *); static int process_ov_record (char *, krb5_context, FILE *, int, int *); typedef krb5_error_code (*load_func)(char *, krb5_context, FILE *, int, int *); typedef struct _dump_version { char *name; char *header; int updateonly; int create_kadm5; dump_func dump_princ; osa_adb_iter_policy_func dump_policy; load_func load_record; } dump_version; dump_version old_version = { "Kerberos version 5 old format", "kdb5_edit load_dump version 2.0\n", 0, 1, dump_k5beta_iterator, NULL, process_k5beta_record }; dump_version beta6_version = { "Kerberos version 5 beta 6 format", "kdb5_edit load_dump version 3.0\n", 0, 1, dump_k5beta6_iterator, NULL, process_k5beta6_record }; dump_version beta7_version = { "Kerberos version 5", "kdb5_util load_dump version 4\n", 0, 0, dump_k5beta7_princ, dump_k5beta7_policy, process_k5beta7_record }; dump_version iprop_version = { "Kerberos iprop version", "iprop", 0, 0, dump_iprop_princ, dump_k5beta7_policy, process_k5beta7_record }; dump_version ov_version = { "OpenV*Secure V1.0", "OpenV*Secure V1.0\t", 1, 1, dump_ov_princ, dump_k5beta7_policy, process_ov_record }; dump_version r1_3_version = { "Kerberos version 5 release 1.3", "kdb5_util load_dump version 5\n", 0, 0, dump_k5beta7_princ_withpolicy, dump_k5beta7_policy, process_k5beta7_record, }; /* External data */ extern char *current_dbname; extern krb5_boolean dbactive; extern int exit_status; extern krb5_context util_context; extern kadm5_config_params global_params; /* Strings */ #define k5beta_dump_header "kdb5_edit load_dump version 2.0\n" static const char null_mprinc_name[] = "kdb5_dump@MISSING"; /* * We define gettext(s) to be s here, so that xgettext will extract the * strings to the .po file. At the end of the message section we will * undef gettext so that we can use it as a funtion. */ #define gettext(s) s /* Message strings */ static const char regex_err[] = gettext("%s: regular expression error - %s\n"); static const char regex_merr[] = gettext("%s: regular expression match error - %s\n"); static const char pname_unp_err[] = gettext("%s: cannot unparse principal name (%s)\n"); static const char mname_unp_err[] = gettext("%s: cannot unparse modifier name (%s)\n"); static const char nokeys_err[] = gettext("%s: cannot find any standard key for %s\n"); static const char sdump_tl_inc_err[] = gettext("%s: tagged data list inconsistency for %s " "(counted %d, stored %d)\n"); static const char stand_fmt_name[] = gettext("Kerberos version 5"); static const char old_fmt_name[] = gettext("Kerberos version 5 old format"); static const char b6_fmt_name[] = gettext("Kerberos version 5 beta 6 format"); static const char ofopen_error[] = gettext("%s: cannot open %s for writing (%s)\n"); static const char oflock_error[] = gettext("%s: cannot lock %s (%s)\n"); static const char dumprec_err[] = gettext("%s: error performing %s dump (%s)\n"); static const char dumphdr_err[] = gettext("%s: error dumping %s header (%s)\n"); static const char trash_end_fmt[] = gettext("%s(%d): ignoring trash at end of line: "); static const char read_name_string[] = gettext("name string"); static const char read_key_type[] = gettext("key type"); static const char read_key_data[] = gettext("key data"); static const char read_pr_data1[] = gettext("first set of principal attributes"); static const char read_mod_name[] = gettext("modifier name"); static const char read_pr_data2[] = gettext("second set of principal attributes"); static const char read_salt_data[] = gettext("salt data"); static const char read_akey_type[] = gettext("alternate key type"); static const char read_akey_data[] = gettext("alternate key data"); static const char read_asalt_type[] = gettext("alternate salt type"); static const char read_asalt_data[] = gettext("alternate salt data"); static const char read_exp_data[] = gettext("expansion data"); static const char store_err_fmt[] = gettext("%s(%d): cannot store %s(%s)\n"); static const char add_princ_fmt[] = gettext("%s\n"); static const char parse_err_fmt[] = gettext("%s(%d): cannot parse %s (%s)\n"); static const char read_err_fmt[] = gettext("%s(%d): cannot read %s\n"); static const char no_mem_fmt[] = gettext("%s(%d): no memory for buffers\n"); static const char rhead_err_fmt[] = gettext("%s(%d): cannot match size tokens\n"); static const char err_line_fmt[] = gettext("%s: error processing line %d of %s\n"); static const char head_bad_fmt[] = gettext("%s: dump header bad in %s\n"); static const char read_bytecnt[] = gettext("record byte count"); static const char read_encdata[] = gettext("encoded data"); static const char n_name_unp_fmt[] = gettext("%s(%s): cannot unparse name\n"); static const char n_dec_cont_fmt[] = gettext("%s(%s): cannot decode contents\n"); static const char read_nint_data[] = gettext("principal static attributes"); static const char read_tcontents[] = gettext("tagged data contents"); static const char read_ttypelen[] = gettext("tagged data type and length"); static const char read_kcontents[] = gettext("key data contents"); static const char read_ktypelen[] = gettext("key data type and length"); static const char read_econtents[] = gettext("extra data contents"); static const char k5beta_fmt_name[] = gettext("Kerberos version 5 old format"); static const char standard_fmt_name[] = gettext("Kerberos version 5 format"); static const char no_name_mem_fmt[] = gettext("%s: cannot get memory for temporary name\n"); static const char ctx_err_fmt[] = gettext("%s: cannot initialize Kerberos context\n"); static const char stdin_name[] = gettext("standard input"); static const char remaster_err_fmt[] = gettext("while re-encoding keys for principal %s with new master key"); static const char restfail_fmt[] = gettext("%s: %s restore failed\n"); static const char close_err_fmt[] = gettext("%s: cannot close database (%s)\n"); static const char dbinit_err_fmt[] = gettext("%s: cannot initialize database (%s)\n"); static const char dblock_err_fmt[] = gettext("%s: cannot initialize database lock (%s)\n"); static const char dbname_err_fmt[] = gettext("%s: cannot set database name to %s (%s)\n"); static const char dbdelerr_fmt[] = gettext("%s: cannot delete bad database %s (%s)\n"); static const char dbunlockerr_fmt[] = gettext("%s: cannot unlock database %s (%s)\n"); static const char dbrenerr_fmt[] = gettext("%s: cannot rename database %s to %s (%s)\n"); static const char dbcreaterr_fmt[] = gettext("%s: cannot create database %s (%s)\n"); static const char dfile_err_fmt[] = gettext("%s: cannot open %s (%s)\n"); /* * We now return you to your regularly scheduled program. */ #undef gettext static const char oldoption[] = "-old"; static const char b6option[] = "-b6"; static const char b7option[] = "-b7"; static const char ipropoption[] = "-i"; static const char verboseoption[] = "-verbose"; static const char updateoption[] = "-update"; static const char hashoption[] = "-hash"; static const char ovoption[] = "-ov"; static const char dump_tmptrail[] = "~"; /* * Re-encrypt the key_data with the new master key... */ static krb5_error_code master_key_convert(context, db_entry) krb5_context context; krb5_db_entry * db_entry; { krb5_error_code retval; krb5_keyblock v5plainkey, *key_ptr; krb5_keysalt keysalt; int i, j; krb5_key_data new_key_data, *key_data; krb5_boolean is_mkey; is_mkey = krb5_principal_compare(context, master_princ, db_entry->princ); if (is_mkey && db_entry->n_key_data != 1) fprintf(stderr, gettext( "Master key db entry has %d keys, expecting only 1!\n"), db_entry->n_key_data); for (i=0; i < db_entry->n_key_data; i++) { key_data = &db_entry->key_data[i]; retval = krb5_dbekd_decrypt_key_data(context, &master_key, key_data, &v5plainkey, &keysalt); if (retval) return retval; memset(&new_key_data, 0, sizeof(new_key_data)); key_ptr = is_mkey ? &new_master_key : &v5plainkey; retval = krb5_dbekd_encrypt_key_data(context, &new_master_key, key_ptr, &keysalt, key_data->key_data_kvno, &new_key_data); if (retval) return retval; krb5_free_keyblock_contents(context, &v5plainkey); for (j = 0; j < key_data->key_data_ver; j++) { if (key_data->key_data_length[j]) { free(key_data->key_data_contents[j]); } } *key_data = new_key_data; } return 0; } /* * Update the "ok" file. */ void update_ok_file (file_name) char *file_name; { /* handle slave locking/failure stuff */ char *file_ok; int fd; static char ok[]=".dump_ok"; if ((file_ok = (char *)malloc(strlen(file_name) + strlen(ok) + 1)) == NULL) { com_err(progname, ENOMEM, gettext("while allocating filename " "for update_ok_file")); exit_status++; return; } strcpy(file_ok, file_name); strcat(file_ok, ok); if ((fd = open(file_ok, O_WRONLY|O_CREAT|O_TRUNC, 0600)) < 0) { com_err(progname, errno, gettext("while creating 'ok' file, '%s'"), file_ok); exit_status++; free(file_ok); return; } if (write(fd, "", 1) != 1) { com_err(progname, errno, gettext("while writing to 'ok' file, '%s'"), file_ok); exit_status++; free(file_ok); return; } free(file_ok); close(fd); return; } /* * name_matches() - See if a principal name matches a regular expression * or string. */ static int name_matches(name, arglist) char *name; struct dump_args *arglist; { #if HAVE_REGCOMP regex_t match_exp; regmatch_t match_match; int match_error; char match_errmsg[BUFSIZ]; size_t errmsg_size; #elif HAVE_REGEXP_H char regexp_buffer[RE_BUF_SIZE]; #elif HAVE_RE_COMP extern char *re_comp(); char *re_result; #endif /* HAVE_RE_COMP */ int i, match; /* * Plow, brute force, through the list of names/regular expressions. */ match = (arglist->nnames) ? 0 : 1; for (i=0; innames; i++) { #if HAVE_REGCOMP /* * Compile the regular expression. */ match_error = regcomp(&match_exp, arglist->names[i], REG_EXTENDED); if (match_error) { errmsg_size = regerror(match_error, &match_exp, match_errmsg, sizeof(match_errmsg)); fprintf(stderr, gettext(regex_err), arglist->programname, match_errmsg); break; } /* * See if we have a match. */ match_error = regexec(&match_exp, name, 1, &match_match, 0); if (match_error) { if (match_error != REG_NOMATCH) { errmsg_size = regerror(match_error, &match_exp, match_errmsg, sizeof(match_errmsg)); fprintf(stderr, gettext(regex_merr), arglist->programname, match_errmsg); break; } } else { /* * We have a match. See if it matches the whole * name. */ if ((match_match.rm_so == 0) && (match_match.rm_eo == strlen(name))) match = 1; } regfree(&match_exp); #elif HAVE_REGEXP_H /* * Compile the regular expression. */ compile(arglist->names[i], regexp_buffer, ®exp_buffer[RE_BUF_SIZE], '\0'); if (step(name, regexp_buffer)) { if ((loc1 == name) && (loc2 == &name[strlen(name)])) match = 1; } #elif HAVE_RE_COMP /* * Compile the regular expression. */ if (re_result = re_comp(arglist->names[i])) { fprintf(stderr, gettext(regex_err), arglist->programname, re_result); break; } if (re_exec(name)) match = 1; #else /* HAVE_RE_COMP */ /* * If no regular expression support, then just compare the strings. */ if (!strcmp(arglist->names[i], name)) match = 1; #endif /* HAVE_REGCOMP */ if (match) break; } return(match); } static krb5_error_code find_enctype(dbentp, enctype, salttype, kentp) krb5_db_entry *dbentp; krb5_enctype enctype; krb5_int32 salttype; krb5_key_data **kentp; { int i; int maxkvno; krb5_key_data *datap; maxkvno = -1; datap = (krb5_key_data *) NULL; for (i=0; in_key_data; i++) { if (( (krb5_enctype)dbentp->key_data[i].key_data_type[0] == enctype) && ((dbentp->key_data[i].key_data_type[1] == salttype) || (salttype < 0))) { maxkvno = dbentp->key_data[i].key_data_kvno; datap = &dbentp->key_data[i]; } } if (maxkvno >= 0) { *kentp = datap; return(0); } return(ENOENT); } #if 0 /* * dump_k5beta_header() - Make a dump header that is recognizable by Kerberos * Version 5 Beta 5 and previous releases. */ static krb5_error_code dump_k5beta_header(arglist) struct dump_args *arglist; { /* The old header consists of the leading string */ fprintf(arglist->ofile, k5beta_dump_header); return(0); } #endif /* * dump_k5beta_iterator() - Dump an entry in a format that is usable * by Kerberos Version 5 Beta 5 and previous * releases. */ static krb5_error_code dump_k5beta_iterator(ptr, entry) krb5_pointer ptr; krb5_db_entry *entry; { krb5_error_code retval; struct dump_args *arg; char *name, *mod_name; krb5_principal mod_princ; krb5_key_data *pkey, *akey, nullkey; krb5_timestamp mod_date, last_pwd_change; int i; /* Initialize */ arg = (struct dump_args *) ptr; name = (char *) NULL; mod_name = (char *) NULL; memset(&nullkey, 0, sizeof(nullkey)); /* * Flatten the principal name. */ if ((retval = krb5_unparse_name(arg->kcontext, entry->princ, &name))) { fprintf(stderr, gettext(pname_unp_err), arg->programname, error_message(retval)); return(retval); } /* * Re-encode the keys in the new master key, if necessary. */ if (mkey_convert) { retval = master_key_convert(arg->kcontext, entry); if (retval) { com_err(arg->programname, retval, remaster_err_fmt, name); return retval; } } /* * If we don't have any match strings, or if our name matches, then * proceed with the dump, otherwise, just forget about it. */ if (!arg->nnames || name_matches(name, arg)) { /* * Deserialize the modifier record. */ mod_name = (char *) NULL; mod_princ = NULL; last_pwd_change = mod_date = 0; pkey = akey = (krb5_key_data *) NULL; if (!(retval = krb5_dbe_lookup_mod_princ_data(arg->kcontext, entry, &mod_date, &mod_princ))) { if (mod_princ) { /* * Flatten the modifier name. */ if ((retval = krb5_unparse_name(arg->kcontext, mod_princ, &mod_name))) fprintf(stderr, gettext(mname_unp_err), arg->programname, error_message(retval)); krb5_free_principal(arg->kcontext, mod_princ); } } if (!mod_name) mod_name = strdup(null_mprinc_name); /* * Find the last password change record and set it straight. */ if ((retval = krb5_dbe_lookup_last_pwd_change(arg->kcontext, entry, &last_pwd_change))) { fprintf(stderr, gettext(nokeys_err), arg->programname, name); krb5_xfree(mod_name); krb5_xfree(name); return(retval); } /* * Find the 'primary' key and the 'alternate' key. */ if ((retval = find_enctype(entry, ENCTYPE_DES_CBC_CRC, KRB5_KDB_SALTTYPE_NORMAL, &pkey)) && (retval = find_enctype(entry, ENCTYPE_DES_CBC_CRC, KRB5_KDB_SALTTYPE_V4, &akey))) { fprintf(stderr, gettext(nokeys_err), arg->programname, name); krb5_xfree(mod_name); krb5_xfree(name); return(retval); } /* If we only have one type, then ship it out as the primary. */ if (!pkey && akey) { pkey = akey; akey = &nullkey; } else { if (!akey) akey = &nullkey; } /* * First put out strings representing the length of the variable * length data in this record, then the name and the primary key type. */ fprintf(arg->ofile, "%d\t%d\t%d\t%d\t%d\t%d\t%s\t%d\t", strlen(name), strlen(mod_name), (krb5_int32) pkey->key_data_length[0], (krb5_int32) akey->key_data_length[0], (krb5_int32) pkey->key_data_length[1], (krb5_int32) akey->key_data_length[1], name, (krb5_int32) pkey->key_data_type[0]); for (i=0; ikey_data_length[0]; i++) { fprintf(arg->ofile, "%02x", pkey->key_data_contents[0][i]); } /* * Second, print out strings representing the standard integer * data in this record. */ fprintf(arg->ofile, "\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%s\t%u\t%u\t%u\t", (krb5_int32) pkey->key_data_kvno, entry->max_life, entry->max_renewable_life, 1 /* Fake mkvno */, entry->expiration, entry->pw_expiration, last_pwd_change, entry->last_success, entry->last_failed, entry->fail_auth_count, mod_name, mod_date, entry->attributes, pkey->key_data_type[1]); /* Pound out the salt data, if present. */ for (i=0; ikey_data_length[1]; i++) { fprintf(arg->ofile, "%02x", pkey->key_data_contents[1][i]); } /* Pound out the alternate key type and contents */ fprintf(arg->ofile, "\t%u\t", akey->key_data_type[0]); for (i=0; ikey_data_length[0]; i++) { fprintf(arg->ofile, "%02x", akey->key_data_contents[0][i]); } /* Pound out the alternate salt type and contents */ fprintf(arg->ofile, "\t%u\t", akey->key_data_type[1]); for (i=0; ikey_data_length[1]; i++) { fprintf(arg->ofile, "%02x", akey->key_data_contents[1][i]); } /* Pound out the expansion data. (is null) */ for (i=0; i < 8; i++) { fprintf(arg->ofile, "\t%u", 0); } fprintf(arg->ofile, ";\n"); /* If we're blabbing, do it */ if (arg->verbose) fprintf(stderr, "%s\n", name); krb5_xfree(mod_name); } krb5_xfree(name); return(0); } /* * dump_k5beta6_iterator() - Output a dump record in krb5b6 format. */ static krb5_error_code dump_k5beta6_iterator(ptr, entry) krb5_pointer ptr; krb5_db_entry *entry; { return dump_k5beta6_iterator_ext(ptr, entry, 0); } static krb5_error_code dump_k5beta6_iterator_ext(ptr, entry, kadm) krb5_pointer ptr; krb5_db_entry *entry; int kadm; { krb5_error_code retval; struct dump_args *arg; char *name; krb5_tl_data *tlp; krb5_key_data *kdata; int counter, skip, i, j; /* Initialize */ arg = (struct dump_args *) ptr; name = (char *) NULL; /* * Flatten the principal name. */ if ((retval = krb5_unparse_name(arg->kcontext, entry->princ, &name))) { fprintf(stderr, gettext(pname_unp_err), arg->programname, error_message(retval)); return(retval); } /* * Re-encode the keys in the new master key, if necessary. */ if (mkey_convert) { retval = master_key_convert(arg->kcontext, entry); if (retval) { com_err(arg->programname, retval, remaster_err_fmt, name); return retval; } } /* * If we don't have any match strings, or if our name matches, then * proceed with the dump, otherwise, just forget about it. */ if (!arg->nnames || name_matches(name, arg)) { /* * We'd like to just blast out the contents as they would appear in * the database so that we can just suck it back in, but it doesn't * lend itself to easy editing. */ /* * The dump format is as follows: * len strlen(name) n_tl_data n_key_data e_length * name * attributes max_life max_renewable_life expiration * pw_expiration last_success last_failed fail_auth_count * n_tl_data*[type length ] * n_key_data*[ver kvno ver*(type length )] * * Fields which are not encapsulated by angle-brackets are to appear * verbatim. A bracketed field's absence is indicated by a -1 in its * place */ /* * Make sure that the tagged list is reasonably correct. */ counter = skip = 0; for (tlp = entry->tl_data; tlp; tlp = tlp->tl_data_next) { /* * don't dump tl data types we know aren't understood by * earlier revisions [krb5-admin/89] */ switch (tlp->tl_data_type) { case KRB5_TL_KADM_DATA: if (kadm) counter++; else skip++; break; default: counter++; break; } } if (counter + skip == entry->n_tl_data) { /* Pound out header */ fprintf(arg->ofile, "%d\t%d\t%d\t%d\t%d\t%s\t", (int) entry->len, strlen(name), counter, (int) entry->n_key_data, (int) entry->e_length, name); fprintf(arg->ofile, "%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t", entry->attributes, entry->max_life, entry->max_renewable_life, entry->expiration, entry->pw_expiration, entry->last_success, entry->last_failed, entry->fail_auth_count); /* Pound out tagged data. */ for (tlp = entry->tl_data; tlp; tlp = tlp->tl_data_next) { if (tlp->tl_data_type == KRB5_TL_KADM_DATA && !kadm) continue; /* see above, [krb5-admin/89] */ fprintf(arg->ofile, "%d\t%d\t", (int) tlp->tl_data_type, (int) tlp->tl_data_length); if (tlp->tl_data_length) for (i=0; itl_data_length; i++) fprintf(arg->ofile, "%02x", tlp->tl_data_contents[i]); else fprintf(arg->ofile, "%d", -1); fprintf(arg->ofile, "\t"); } /* Pound out key data */ for (counter=0; countern_key_data; counter++) { kdata = &entry->key_data[counter]; fprintf(arg->ofile, "%d\t%d\t", (int) kdata->key_data_ver, (int) kdata->key_data_kvno); for (i=0; ikey_data_ver; i++) { fprintf(arg->ofile, "%d\t%d\t", kdata->key_data_type[i], kdata->key_data_length[i]); if (kdata->key_data_length[i]) for (j=0; jkey_data_length[i]; j++) fprintf(arg->ofile, "%02x", kdata->key_data_contents[i][j]); else fprintf(arg->ofile, "%d", -1); fprintf(arg->ofile, "\t"); } } /* Pound out extra data */ if (entry->e_length) for (i=0; ie_length; i++) fprintf(arg->ofile, "%02x", entry->e_data[i]); else fprintf(arg->ofile, "%d", -1); /* Print trailer */ fprintf(arg->ofile, ";\n"); if (arg->verbose) fprintf(stderr, "%s\n", name); } else { fprintf(stderr, gettext(sdump_tl_inc_err), arg->programname, name, counter+skip, (int) entry->n_tl_data); retval = EINVAL; } } krb5_xfree(name); return(retval); } /* * dump_iprop_iterator() - Output a dump record in iprop format. */ static krb5_error_code dump_iprop_iterator(ptr, entry) krb5_pointer ptr; krb5_db_entry *entry; { krb5_error_code retval; struct dump_args *arg; char *name; krb5_tl_data *tlp; krb5_key_data *kdata; int counter, i, j; /* Initialize */ arg = (struct dump_args *) ptr; name = (char *) NULL; /* * Flatten the principal name. */ if ((retval = krb5_unparse_name(arg->kcontext, entry->princ, &name))) { fprintf(stderr, gettext(pname_unp_err), arg->programname, error_message(retval)); return(retval); } /* * Re-encode the keys in the new master key, if necessary. */ if (mkey_convert) { retval = master_key_convert(arg->kcontext, entry); if (retval) { com_err(arg->programname, retval, remaster_err_fmt, name); return retval; } } /* * If we don't have any match strings, or if our name matches, then * proceed with the dump, otherwise, just forget about it. */ if (!arg->nnames || name_matches(name, arg)) { /* * We'd like to just blast out the contents as they would * appear in the database so that we can just suck it back * in, but it doesn't lend itself to easy editing. */ /* * The dump format is as follows: len strlen(name) * n_tl_data n_key_data e_length name attributes max_life * max_renewable_life expiration pw_expiration last_success * last_failed fail_auth_count n_tl_data*[type length * ] n_key_data*[ver kvno ver*(type length * )] Fields which are not encapsulated * by angle-brackets are to appear verbatim. Bracketed * fields absence is indicated by a -1 in its place */ /* * Make sure that the tagged list is reasonably correct. */ counter = 0; for (tlp = entry->tl_data; tlp; tlp = tlp->tl_data_next) counter++; if (counter == entry->n_tl_data) { /* Pound out header */ fprintf(arg->ofile, "%d\t%d\t%d\t%d\t%d\t%s\t", (int) entry->len, strlen(name), (int) entry->n_tl_data, (int) entry->n_key_data, (int) entry->e_length, name); fprintf(arg->ofile, "%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t", entry->attributes, entry->max_life, entry->max_renewable_life, entry->expiration, entry->pw_expiration, entry->last_success, entry->last_failed, entry->fail_auth_count); /* Pound out tagged data. */ for (tlp = entry->tl_data; tlp; tlp = tlp->tl_data_next) { fprintf(arg->ofile, "%d\t%d\t", (int) tlp->tl_data_type, (int) tlp->tl_data_length); if (tlp->tl_data_length) for (i = 0; i < tlp->tl_data_length; i++) fprintf(arg->ofile, "%02x", tlp-> tl_data_contents[i]); else fprintf(arg->ofile, "%d", -1); fprintf(arg->ofile, "\t"); } /* Pound out key data */ for (counter = 0; counter < entry->n_key_data; counter++) { kdata = &entry->key_data[counter]; fprintf(arg->ofile, "%d\t%d\t", (int) kdata->key_data_ver, (int) kdata->key_data_kvno); for (i=0; ikey_data_ver; i++) { fprintf(arg->ofile, "%d\t%d\t", kdata->key_data_type[i], kdata->key_data_length[i]); if (kdata->key_data_length[i]) for (j = 0; j < kdata-> key_data_length[i]; j++) fprintf(arg->ofile, "%02x", kdata-> key_data_contents [i][j]); else fprintf(arg->ofile, "%d", -1); fprintf(arg->ofile, "\t"); } } /* Pound out extra data */ if (entry->e_length) for (i=0; ie_length; i++) fprintf(arg->ofile, "%02x", entry->e_data[i]); else fprintf(arg->ofile, "%d", -1); /* Print trailer */ fprintf(arg->ofile, ";\n"); if (arg->verbose) fprintf(stderr, "%s\n", name); } else { fprintf(stderr, gettext(sdump_tl_inc_err), arg->programname, name, counter, (int) entry->n_tl_data); retval = EINVAL; } } krb5_xfree(name); return(retval); } /* * dump_k5beta7_iterator() - Output a dump record in krb5b7 format. */ static krb5_error_code dump_k5beta7_princ(ptr, entry) krb5_pointer ptr; krb5_db_entry *entry; { return dump_k5beta7_princ_ext(ptr, entry, 0); } static krb5_error_code dump_k5beta7_princ_ext(ptr, entry, kadm) krb5_pointer ptr; krb5_db_entry *entry; int kadm; { krb5_error_code retval; struct dump_args *arg; char *name; int tmp_nnames; /* Initialize */ arg = (struct dump_args *) ptr; name = (char *) NULL; /* * Flatten the principal name. */ if ((retval = krb5_unparse_name(arg->kcontext, entry->princ, &name))) { fprintf(stderr, gettext(pname_unp_err), arg->programname, error_message(retval)); return(retval); } /* * If we don't have any match strings, or if our name matches, then * proceed with the dump, otherwise, just forget about it. */ if (!arg->nnames || name_matches(name, arg)) { fprintf(arg->ofile, "princ\t"); /* save the callee from matching the name again */ tmp_nnames = arg->nnames; arg->nnames = 0; retval = dump_k5beta6_iterator_ext(ptr, entry, kadm); arg->nnames = tmp_nnames; } free(name); return retval; } /* * dump_iprop_princ() - Output a dump record in iprop format. * This was created in order to dump more data, such as kadm5 tl */ static krb5_error_code dump_iprop_princ(ptr, entry) krb5_pointer ptr; krb5_db_entry *entry; { krb5_error_code retval; struct dump_args *arg; char *name; int tmp_nnames; /* Initialize */ arg = (struct dump_args *) ptr; name = (char *) NULL; /* * Flatten the principal name. */ if ((retval = krb5_unparse_name(arg->kcontext, entry->princ, &name))) { fprintf(stderr, gettext(pname_unp_err), arg->programname, error_message(retval)); return(retval); } /* * If we don't have any match strings, or if our name matches, then * proceed with the dump, otherwise, just forget about it. */ if (!arg->nnames || name_matches(name, arg)) { fprintf(arg->ofile, "princ\t"); /* save the callee from matching the name again */ tmp_nnames = arg->nnames; arg->nnames = 0; retval = dump_iprop_iterator(ptr, entry); arg->nnames = tmp_nnames; } free(name); return (retval); } static krb5_error_code dump_k5beta7_princ_withpolicy(ptr, entry) krb5_pointer ptr; krb5_db_entry *entry; { return dump_k5beta7_princ_ext(ptr, entry, 1); } void dump_k5beta7_policy(void *data, osa_policy_ent_t entry) { struct dump_args *arg; arg = (struct dump_args *) data; fprintf(arg->ofile, "policy\t%s\t%d\t%d\t%d\t%d\t%d\t%d\n", entry->name, entry->pw_min_life, entry->pw_max_life, entry->pw_min_length, entry->pw_min_classes, entry->pw_history_num, entry->policy_refcnt); } static void print_key_data(FILE *f, krb5_key_data *key_data) { int c; fprintf(f, "%d\t%d\t", key_data->key_data_type[0], key_data->key_data_length[0]); for(c = 0; c < key_data->key_data_length[0]; c++) fprintf(f, "%02x ", key_data->key_data_contents[0][c]); } /* * Function: print_princ * * Purpose: output osa_adb_princ_ent data in a human * readable format (which is a format suitable for * ovsec_adm_import consumption) * * Arguments: * data (input) pointer to a structure containing a FILE * * and a record counter. * entry (input) entry to get dumped. * void * * Requires: * nuttin * * Effects: * writes data to the specified file pointerp. * * Modifies: * nuttin * */ static krb5_error_code dump_ov_princ(krb5_pointer ptr, krb5_db_entry *kdb) { char *princstr; int x, y, foundcrc; struct dump_args *arg; krb5_tl_data tl_data; osa_princ_ent_rec adb; XDR xdrs; arg = (struct dump_args *) ptr; /* * XXX Currently, lookup_tl_data always returns zero; it sets * tl_data->tl_data_length to zero if the type isn't found. * This should be fixed... */ /* * XXX Should this function do nothing for a principal with no * admin data, or print a record of "default" values? See * comment in server_kdb.c to help decide. */ tl_data.tl_data_type = KRB5_TL_KADM_DATA; if (krb5_dbe_lookup_tl_data(arg->kcontext, kdb, &tl_data) || (tl_data.tl_data_length == 0)) return 0; memset(&adb, 0, sizeof(adb)); xdrmem_create(&xdrs, (const caddr_t) tl_data.tl_data_contents, tl_data.tl_data_length, XDR_DECODE); if (! xdr_osa_princ_ent_rec(&xdrs, &adb)) { xdr_destroy(&xdrs); return(KADM5_XDR_FAILURE); } xdr_destroy(&xdrs); krb5_unparse_name(arg->kcontext, kdb->princ, &princstr); fprintf(arg->ofile, "princ\t%s\t", princstr); if(adb.policy == NULL) fputc('\t', arg->ofile); else fprintf(arg->ofile, "%s\t", adb.policy); fprintf(arg->ofile, "%lx\t%d\t%d\t%d", adb.aux_attributes, adb.old_key_len,adb.old_key_next, adb.admin_history_kvno); for (x = 0; x < adb.old_key_len; x++) { foundcrc = 0; for (y = 0; y < adb.old_keys[x].n_key_data; y++) { krb5_key_data *key_data = &adb.old_keys[x].key_data[y]; if (key_data->key_data_type[0] != ENCTYPE_DES_CBC_CRC) continue; if (foundcrc) { fprintf(stderr, gettext("Warning! Multiple DES-CBC-CRC " "keys for principal %s; skipping " "duplicates.\n"), princstr); continue; } foundcrc++; fputc('\t', arg->ofile); print_key_data(arg->ofile, key_data); } if (!foundcrc) fprintf(stderr, gettext("Warning! No DES-CBC-CRC key " "for principal %s, cannot generate " "OV-compatible record; skipping\n"), princstr); } fputc('\n', arg->ofile); free(princstr); return 0; } /* * usage is: * dump_db [-i] [-old] [-b6] [-b7] [-ov] [-verbose] [-mkey_convert] * [-new_mkey_file mkey_file] [-rev] [-recurse] * [filename [principals...]] */ void dump_db(argc, argv) int argc; char **argv; { FILE *f; struct dump_args arglist; /* Solaris Kerberos */ #if 0 char *programname; #endif char *ofile; krb5_error_code kret, retval; dump_version *dump; int aindex; krb5_boolean locked; char *new_mkey_file = 0; bool_t dump_sno = FALSE; kdb_log_context *log_ctx; /* Solaris Kerberos: adding support for -rev/recurse flags */ int db_arg_index = 0; char *db_args[3] = {NULL, NULL, NULL}; /* * Parse the arguments. */ /* Solaris Kerberos */ #if 0 programname = argv[0]; if (strrchr(programname, (int) '/')) programname = strrchr(argv[0], (int) '/') + 1; #endif ofile = (char *) NULL; dump = &r1_3_version; arglist.verbose = 0; new_mkey_file = 0; mkey_convert = 0; backwards = 0; recursive = 0; log_ctx = util_context->kdblog_context; /* * Parse the qualifiers. */ for (aindex = 1; aindex < argc; aindex++) { if (!strcmp(argv[aindex], oldoption)) dump = &old_version; else if (!strcmp(argv[aindex], b6option)) dump = &beta6_version; else if (!strcmp(argv[aindex], b7option)) dump = &beta7_version; else if (!strcmp(argv[aindex], ovoption)) dump = &ov_version; else if (!strcmp(argv[aindex], ipropoption)) { if (log_ctx && log_ctx->iproprole) { dump = &iprop_version; /* * dump_sno is used to indicate if the serial * # should be populated in the output * file to be used later by iprop for updating * the slave's update log when loading */ dump_sno = TRUE; } else { fprintf(stderr, gettext("Iprop not enabled\n")); exit_status++; return; } } else if (!strcmp(argv[aindex], verboseoption)) arglist.verbose++; else if (!strcmp(argv[aindex], "-mkey_convert")) mkey_convert = 1; else if (!strcmp(argv[aindex], "-new_mkey_file")) { new_mkey_file = argv[++aindex]; mkey_convert = 1; } else if (!strcmp(argv[aindex], "-rev")) { /* Solaris Kerberos: adding support for -rev/recurse flags */ /* hack to pass args to db specific plugin */ db_args[db_arg_index++] = "rev"; } else if (!strcmp(argv[aindex], "-recurse")) { /* hack to pass args to db specific plugin */ db_args[db_arg_index++] = "recurse"; } else break; } arglist.names = (char **) NULL; arglist.nnames = 0; if (aindex < argc) { ofile = argv[aindex]; aindex++; if (aindex < argc) { arglist.names = &argv[aindex]; arglist.nnames = argc - aindex; } } /* * Make sure the database is open. The policy database only has * to be opened if we try a dump that uses it. */ if (!dbactive) { /* Solaris Kerberos */ com_err(progname, 0, Err_no_database); /* Solaris Kerberos */ exit_status++; return; } /* * If we're doing a master key conversion, set up for it. */ if (mkey_convert) { if (!valid_master_key) { /* TRUE here means read the keyboard, but only once */ retval = krb5_db_fetch_mkey(util_context, master_princ, global_params.enctype, TRUE, FALSE, (char *) NULL, 0, &master_key); if (retval) { /* Solaris Kerberos */ com_err(progname, retval, gettext("while reading master key")); exit(1); } retval = krb5_db_verify_master_key(util_context, master_princ, &master_key); if (retval) { /* Solaris Kerberos */ com_err(progname, retval, gettext("while verifying master key")); exit(1); } } if (!new_mkey_file) printf(gettext("Please enter new master key....\n")); if ((retval = krb5_db_fetch_mkey(util_context, master_princ, global_params.enctype, (new_mkey_file == 0) ? (krb5_boolean) 1 : 0, TRUE, new_mkey_file, 0, &new_master_key))) { /* Solaris Kerberos */ com_err(progname, retval, gettext("while reading new master key")); exit(1); } } kret = 0; locked = 0; if (ofile && strcmp(ofile, "-")) { /* * Discourage accidental dumping to filenames beginning with '-'. */ if (ofile[0] == '-') usage(); /* * Make sure that we don't open and truncate on the fopen, * since that may hose an on-going kprop process. * * We could also control this by opening for read and * write, doing an flock with LOCK_EX, and then * truncating the file once we have gotten the lock, * but that would involve more OS dependencies than I * want to get into. */ unlink(ofile); if (!(f = fopen(ofile, "w"))) { /* Solaris Kerberos */ fprintf(stderr, gettext(ofopen_error), progname, ofile, error_message(errno)); exit_status++; return; } if ((kret = krb5_lock_file(util_context, fileno(f), KRB5_LOCKMODE_EXCLUSIVE))) { /* Solaris Kerberos */ fprintf(stderr, gettext(oflock_error), progname, ofile, error_message(kret)); exit_status++; } else locked = 1; } else { f = stdout; } if (f && !(kret)) { /* Solaris Kerberos */ arglist.programname = progname; arglist.ofile = f; arglist.kcontext = util_context; fprintf(arglist.ofile, "%s", dump->header); if (dump_sno) { if (ulog_map(util_context, &global_params, FKCOMMAND)) { /* Solaris Kerberos */ fprintf(stderr, gettext("%s: Could not map log\n"), progname); exit_status++; goto error; } /* * We grab the lock twice (once again in the iterator call), * but that's ok since the lock func handles incr locks held. */ if (krb5_db_lock(util_context, KRB5_LOCKMODE_SHARED)) { /* Solaris Kerberos */ fprintf(stderr, gettext("%s: Couldn't grab lock\n"), progname); exit_status++; goto error; } fprintf(f, " %u", log_ctx->ulog->kdb_last_sno); fprintf(f, " %u", log_ctx->ulog->kdb_last_time.seconds); fprintf(f, " %u", log_ctx->ulog->kdb_last_time.useconds); } if (dump->header[strlen(dump->header)-1] != '\n') fputc('\n', arglist.ofile); /* Solaris Kerberos: adding support for -rev/recurse flags */ /* don't pass in db_args if there aren't any */ if ((kret = krb5_db_iterate(util_context, NULL, dump->dump_princ, (krb5_pointer) &arglist, db_arg_index > 0 ? (char **)&db_args : NULL))) { /* Solaris Kerberos */ fprintf(stderr, dumprec_err, progname, dump->name, error_message(kret)); exit_status++; if (dump_sno) (void) krb5_db_unlock(util_context); } if (dump->dump_policy && (kret = krb5_db_iter_policy( util_context, "*", dump->dump_policy, &arglist))) { /* Solaris Kerberos */ fprintf(stderr, gettext(dumprec_err), progname, dump->name, error_message(kret)); exit_status++; } error: if (ofile && f != stdout && !exit_status) { if (locked) { (void) krb5_lock_file(util_context, fileno(f), KRB5_LOCKMODE_UNLOCK); locked = 0; } fclose(f); update_ok_file(ofile); } } if (locked) (void) krb5_lock_file(util_context, fileno(f), KRB5_LOCKMODE_UNLOCK); } /* * Read a string of bytes while counting the number of lines passed. */ static int read_string(f, buf, len, lp) FILE *f; char *buf; int len; int *lp; { int c; int i, retval; retval = 0; for (i=0; itl_data; (pwchg) && (pwchg->tl_data_type != KRB5_TL_LAST_PWD_CHANGE); pwchg = pwchg->tl_data_next); /* Check to see if we found one. */ linked = 0; if (!pwchg) { /* No, allocate a new one */ if ((pwchg = (krb5_tl_data *) malloc(sizeof(krb5_tl_data)))) { memset(pwchg, 0, sizeof(krb5_tl_data)); if (!(pwchg->tl_data_contents = (krb5_octet *) malloc(sizeof(krb5_timestamp)))) { free(pwchg); pwchg = (krb5_tl_data *) NULL; } else { pwchg->tl_data_type = KRB5_TL_LAST_PWD_CHANGE; pwchg->tl_data_length = (krb5_int16) sizeof(krb5_timestamp); } } } else linked = 1; /* Do we have an entry? */ if (pwchg && pwchg->tl_data_contents) { /* Encode it */ krb5_kdb_encode_int32(last_pwd_change, pwchg->tl_data_contents); /* Link it in if necessary */ if (!linked) { pwchg->tl_data_next = dbentp->tl_data; dbentp->tl_data = pwchg; dbentp->n_tl_data++; } } else kret = ENOMEM; } return(kret); } #endif /* * process_k5beta_record() - Handle a dump record in old format. * * Returns -1 for end of file, 0 for success and 1 for failure. */ static int process_k5beta_record(fname, kcontext, filep, verbose, linenop) char *fname; krb5_context kcontext; FILE *filep; int verbose; int *linenop; { int nmatched; int retval; krb5_db_entry dbent; int name_len, mod_name_len, key_len; int alt_key_len, salt_len, alt_salt_len; char *name; char *mod_name; int tmpint1, tmpint2, tmpint3; int error; const char *try2read; int i; krb5_key_data *pkey, *akey; krb5_timestamp last_pwd_change, mod_date; krb5_principal mod_princ; krb5_error_code kret; krb5_octet *shortcopy1 = NULL; /* SUNWresync121 memleak fix */ krb5_octet *shortcopy2 = NULL; try2read = (char *) NULL; (*linenop)++; retval = 1; memset((char *)&dbent, 0, sizeof(dbent)); /* Make sure we've got key_data entries */ if (krb5_dbe_create_key_data(kcontext, &dbent) || krb5_dbe_create_key_data(kcontext, &dbent)) { krb5_db_free_principal(kcontext, &dbent, 1); return(1); } pkey = &dbent.key_data[0]; akey = &dbent.key_data[1]; /* * Match the sizes. 6 tokens to match. */ nmatched = fscanf(filep, "%d\t%d\t%d\t%d\t%d\t%d\t", &name_len, &mod_name_len, &key_len, &alt_key_len, &salt_len, &alt_salt_len); if (nmatched == 6) { pkey->key_data_length[0] = key_len; akey->key_data_length[0] = alt_key_len; pkey->key_data_length[1] = salt_len; akey->key_data_length[1] = alt_salt_len; name = (char *) NULL; mod_name = (char *) NULL; /* * Get the memory for the variable length fields. */ if ((name = (char *) malloc((size_t) (name_len + 1))) && (mod_name = (char *) malloc((size_t) (mod_name_len + 1))) && (!key_len || (pkey->key_data_contents[0] = (krb5_octet *) malloc((size_t) (key_len + 1)))) && (!alt_key_len || (akey->key_data_contents[0] = (krb5_octet *) malloc((size_t) (alt_key_len + 1)))) && (!salt_len || (pkey->key_data_contents[1] = (krb5_octet *) malloc((size_t) (salt_len + 1)))) && (!alt_salt_len || (akey->key_data_contents[1] = (krb5_octet *) malloc((size_t) (alt_salt_len + 1)))) ) { error = 0; /* Read the principal name */ if (read_string(filep, name, name_len, linenop)) { try2read = read_name_string; error++; } /* Read the key type */ if (!error && (fscanf(filep, "\t%d\t", &tmpint1) != 1)) { try2read = read_key_type; error++; } pkey->key_data_type[0] = tmpint1; /* Read the old format key */ if (!error && read_octet_string(filep, pkey->key_data_contents[0], pkey->key_data_length[0])) { try2read = read_key_data; error++; } /* convert to a new format key */ /* the encrypted version is stored as the unencrypted key length (4 bytes, MSB first) followed by the encrypted key. */ if ((pkey->key_data_length[0] > 4) && (pkey->key_data_contents[0][0] == 0) && (pkey->key_data_contents[0][1] == 0)) { /* this really does look like an old key, so drop and swap */ /* the *new* length is 2 bytes, LSB first, sigh. */ size_t shortlen = pkey->key_data_length[0]-4+2; krb5_octet *origdata = pkey->key_data_contents[0]; shortcopy1 = (krb5_octet *) malloc(shortlen); if (shortcopy1) { shortcopy1[0] = origdata[3]; shortcopy1[1] = origdata[2]; memcpy(shortcopy1 + 2, origdata + 4, shortlen - 2); free(origdata); pkey->key_data_length[0] = shortlen; pkey->key_data_contents[0] = shortcopy1; } else { fprintf(stderr, gettext(no_mem_fmt), fname, *linenop); error++; } } /* Read principal attributes */ if (!error && (fscanf(filep, "\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t", &tmpint1, &dbent.max_life, &dbent.max_renewable_life, &tmpint2, &dbent.expiration, &dbent.pw_expiration, &last_pwd_change, &dbent.last_success, &dbent.last_failed, &tmpint3) != 10)) { try2read = read_pr_data1; error++; } pkey->key_data_kvno = tmpint1; dbent.fail_auth_count = tmpint3; /* Read modifier name */ if (!error && read_string(filep, mod_name, mod_name_len, linenop)) { try2read = read_mod_name; error++; } /* Read second set of attributes */ if (!error && (fscanf(filep, "\t%u\t%u\t%u\t", &mod_date, &dbent.attributes, &tmpint1) != 3)) { try2read = read_pr_data2; error++; } pkey->key_data_type[1] = tmpint1; /* Read salt data */ if (!error && read_octet_string(filep, pkey->key_data_contents[1], pkey->key_data_length[1])) { try2read = read_salt_data; error++; } /* Read alternate key type */ if (!error && (fscanf(filep, "\t%u\t", &tmpint1) != 1)) { try2read = read_akey_type; error++; } akey->key_data_type[0] = tmpint1; /* Read alternate key */ if (!error && read_octet_string(filep, akey->key_data_contents[0], akey->key_data_length[0])) { try2read = read_akey_data; error++; } /* convert to a new format key */ /* the encrypted version is stored as the unencrypted key length (4 bytes, MSB first) followed by the encrypted key. */ if ((akey->key_data_length[0] > 4) && (akey->key_data_contents[0][0] == 0) && (akey->key_data_contents[0][1] == 0)) { /* this really does look like an old key, so drop and swap */ /* the *new* length is 2 bytes, LSB first, sigh. */ size_t shortlen = akey->key_data_length[0]-4+2; krb5_octet *origdata = akey->key_data_contents[0]; shortcopy2 = (krb5_octet *) malloc(shortlen); if (shortcopy2) { shortcopy2[0] = origdata[3]; shortcopy2[1] = origdata[2]; memcpy(shortcopy2 + 2, origdata + 4, shortlen - 2); free(origdata); akey->key_data_length[0] = shortlen; akey->key_data_contents[0] = shortcopy2; } else { fprintf(stderr, gettext(no_mem_fmt), fname, *linenop); error++; } } /* Read alternate salt type */ if (!error && (fscanf(filep, "\t%u\t", &tmpint1) != 1)) { try2read = read_asalt_type; error++; } akey->key_data_type[1] = tmpint1; /* Read alternate salt data */ if (!error && read_octet_string(filep, akey->key_data_contents[1], akey->key_data_length[1])) { try2read = read_asalt_data; error++; } /* Read expansion data - discard it */ if (!error) { for (i=0; i<8; i++) { if (fscanf(filep, "\t%u", &tmpint1) != 1) { try2read = read_exp_data; error++; break; } } if (!error) find_record_end(filep, fname, *linenop); } /* * If no error, then we're done reading. Now parse the names * and store the database dbent. */ if (!error) { if (!(kret = krb5_parse_name(kcontext, name, &dbent.princ))) { if (!(kret = krb5_parse_name(kcontext, mod_name, &mod_princ))) { if (!(kret = krb5_dbe_update_mod_princ_data(kcontext, &dbent, mod_date, mod_princ)) && !(kret = krb5_dbe_update_last_pwd_change(kcontext, &dbent, last_pwd_change))) { int one = 1; dbent.len = KRB5_KDB_V1_BASE_LENGTH; pkey->key_data_ver = (pkey->key_data_type[1] || pkey->key_data_length[1]) ? 2 : 1; akey->key_data_ver = (akey->key_data_type[1] || akey->key_data_length[1]) ? 2 : 1; if ((pkey->key_data_type[0] == akey->key_data_type[0]) && (pkey->key_data_type[1] == akey->key_data_type[1])) dbent.n_key_data--; else if ((akey->key_data_type[0] == 0) && (akey->key_data_length[0] == 0) && (akey->key_data_type[1] == 0) && (akey->key_data_length[1] == 0)) dbent.n_key_data--; dbent.mask = KADM5_LOAD | KADM5_PRINCIPAL | KADM5_ATTRIBUTES | KADM5_MAX_LIFE | KADM5_MAX_RLIFE | KADM5_KEY_DATA | KADM5_PRINC_EXPIRE_TIME | KADM5_LAST_SUCCESS | KADM5_LAST_FAILED | KADM5_FAIL_AUTH_COUNT; if ((kret = krb5_db_put_principal(kcontext, &dbent, &one)) || (one != 1)) { fprintf(stderr, gettext(store_err_fmt), fname, *linenop, name, error_message(kret)); error++; } else { if (verbose) fprintf(stderr, gettext(add_princ_fmt), name); retval = 0; } dbent.n_key_data = 2; } krb5_free_principal(kcontext, mod_princ); } else { fprintf(stderr, gettext(parse_err_fmt), fname, *linenop, mod_name, error_message(kret)); error++; } } else { fprintf(stderr, gettext(parse_err_fmt), fname, *linenop, name, error_message(kret)); error++; } } else { fprintf(stderr, gettext(no_mem_fmt), fname, *linenop, try2read); } } else { fprintf(stderr, gettext(read_err_fmt), fname, *linenop); } krb5_db_free_principal(kcontext, &dbent, 1); if (mod_name) free(mod_name); if (name) free(name); } else { if (nmatched != EOF) fprintf(stderr, gettext(rhead_err_fmt), fname, *linenop); else retval = -1; } if (shortcopy1) free(shortcopy1); if (shortcopy2) free(shortcopy2); return(retval); } /* * process_k5beta6_record() - Handle a dump record in krb5b6 format. * * Returns -1 for end of file, 0 for success and 1 for failure. */ static int process_k5beta6_record(fname, kcontext, filep, verbose, linenop) char *fname; krb5_context kcontext; FILE *filep; int verbose; int *linenop; { int retval; krb5_db_entry dbentry; krb5_int32 t1, t2, t3, t4, t5, t6, t7, t8, t9; int nread; int error; int i, j, one; char *name; krb5_key_data *kp, *kdatap; krb5_tl_data **tlp, *tl; krb5_octet *op; krb5_error_code kret; const char *try2read; try2read = (char *) NULL; memset((char *) &dbentry, 0, sizeof(dbentry)); (*linenop)++; retval = 1; name = (char *) NULL; kp = (krb5_key_data *) NULL; op = (krb5_octet *) NULL; error = 0; kret = 0; nread = fscanf(filep, "%d\t%d\t%d\t%d\t%d\t", &t1, &t2, &t3, &t4, &t5); if (nread == 5) { /* Get memory for flattened principal name */ if (!(name = (char *) malloc((size_t) t2 + 1))) error++; /* Get memory for and form tagged data linked list */ tlp = &dbentry.tl_data; for (i=0; itl_data_next); dbentry.n_tl_data++; } else { error++; break; } } /* Get memory for key list */ if (t4 && !(kp = (krb5_key_data *) malloc((size_t) (t4*sizeof(krb5_key_data))))) error++; /* Get memory for extra data */ if (t5 && !(op = (krb5_octet *) malloc((size_t) t5))) error++; if (!error) { dbentry.len = t1; dbentry.n_key_data = t4; dbentry.e_length = t5; if (kp) { memset(kp, 0, (size_t) (t4*sizeof(krb5_key_data))); dbentry.key_data = kp; kp = (krb5_key_data *) NULL; } if (op) { memset(op, 0, (size_t) t5); dbentry.e_data = op; op = (krb5_octet *) NULL; } /* Read in and parse the principal name */ if (!read_string(filep, name, t2, linenop) && !(kret = krb5_parse_name(kcontext, name, &dbentry.princ))) { /* Get the fixed principal attributes */ nread = fscanf(filep, "%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t", &t2, &t3, &t4, &t5, &t6, &t7, &t8, &t9); if (nread == 8) { dbentry.attributes = (krb5_flags) t2; dbentry.max_life = (krb5_deltat) t3; dbentry.max_renewable_life = (krb5_deltat) t4; dbentry.expiration = (krb5_timestamp) t5; dbentry.pw_expiration = (krb5_timestamp) t6; dbentry.last_success = (krb5_timestamp) t7; dbentry.last_failed = (krb5_timestamp) t8; dbentry.fail_auth_count = (krb5_kvno) t9; dbentry.mask = KADM5_LOAD | KADM5_PRINCIPAL | KADM5_ATTRIBUTES | KADM5_MAX_LIFE | KADM5_MAX_RLIFE | KADM5_PRINC_EXPIRE_TIME | KADM5_LAST_SUCCESS | KADM5_LAST_FAILED | KADM5_FAIL_AUTH_COUNT; } else { try2read = read_nint_data; error++; } /* * Get the tagged data. * * Really, this code ought to discard tl data types * that it knows are special to the current version * and were not supported in the previous version. * But it's a pain to implement that here, and doing * it at dump time has almost as good an effect, so * that's what I did. [krb5-admin/89] */ if (!error && dbentry.n_tl_data) { for (tl = dbentry.tl_data; tl; tl = tl->tl_data_next) { nread = fscanf(filep, "%d\t%d\t", &t1, &t2); if (nread == 2) { tl->tl_data_type = (krb5_int16) t1; tl->tl_data_length = (krb5_int16) t2; if (tl->tl_data_length) { if (!(tl->tl_data_contents = (krb5_octet *) malloc((size_t) t2+1)) || read_octet_string(filep, tl->tl_data_contents, t2)) { try2read = read_tcontents; error++; break; } /* test to set mask fields */ if (t1 == KRB5_TL_KADM_DATA) { XDR xdrs; osa_princ_ent_rec osa_princ_ent; /* * Assuming aux_attributes will always be * there */ dbentry.mask |= KADM5_AUX_ATTRIBUTES; /* test for an actual policy reference */ memset(&osa_princ_ent, 0, sizeof(osa_princ_ent)); xdrmem_create(&xdrs, (char *)tl->tl_data_contents, tl->tl_data_length, XDR_DECODE); if (xdr_osa_princ_ent_rec(&xdrs, &osa_princ_ent) && (osa_princ_ent.aux_attributes & KADM5_POLICY) && osa_princ_ent.policy != NULL) { dbentry.mask |= KADM5_POLICY; kdb_free_entry(NULL, NULL, &osa_princ_ent); } xdr_destroy(&xdrs); } } else { /* Should be a null field */ nread = fscanf(filep, "%d", &t9); if ((nread != 1) || (t9 != -1)) { error++; try2read = read_tcontents; break; } } } else { try2read = read_ttypelen; error++; break; } } if (!error) dbentry.mask |= KADM5_TL_DATA; } /* Get the key data */ if (!error && dbentry.n_key_data) { for (i=0; !error && (ikey_data_ver = (krb5_int16) t1; kdatap->key_data_kvno = (krb5_int16) t2; for (j=0; jkey_data_type[j] = t3; kdatap->key_data_length[j] = t4; if (t4) { if (!(kdatap->key_data_contents[j] = (krb5_octet *) malloc((size_t) t4+1)) || read_octet_string(filep, kdatap->key_data_contents[j], t4)) { try2read = read_kcontents; error++; break; } } else { /* Should be a null field */ nread = fscanf(filep, "%d", &t9); if ((nread != 1) || (t9 != -1)) { error++; try2read = read_kcontents; break; } } } else { try2read = read_ktypelen; error++; break; } } } } if (!error) dbentry.mask |= KADM5_KEY_DATA; } /* Get the extra data */ if (!error && dbentry.e_length) { if (read_octet_string(filep, dbentry.e_data, (int) dbentry.e_length)) { try2read = read_econtents; error++; } } else { nread = fscanf(filep, "%d", &t9); if ((nread != 1) || (t9 != -1)) { error++; try2read = read_econtents; } } /* Finally, find the end of the record. */ if (!error) find_record_end(filep, fname, *linenop); /* * We have either read in all the data or choked. */ if (!error) { one = 1; if ((kret = krb5_db_put_principal(kcontext, &dbentry, &one))) { fprintf(stderr, gettext(store_err_fmt), fname, *linenop, name, error_message(kret)); } else { if (verbose) fprintf(stderr, gettext( add_princ_fmt), name); retval = 0; } } else { fprintf(stderr, gettext(read_err_fmt), fname, *linenop, try2read); } } else { if (kret) fprintf(stderr, gettext(parse_err_fmt), fname, *linenop, name, error_message(kret)); else fprintf(stderr, gettext(no_mem_fmt), fname, *linenop); } } else { fprintf(stderr, gettext(rhead_err_fmt), fname, *linenop); } if (op) free(op); if (kp) free(kp); if (name) free(name); krb5_db_free_principal(kcontext, &dbentry, 1); } else { if (nread == EOF) retval = -1; } return(retval); } static int process_k5beta7_policy(fname, kcontext, filep, verbose, linenop, pol_db) char *fname; krb5_context kcontext; FILE *filep; int verbose; int *linenop; void *pol_db; { osa_policy_ent_rec rec; char namebuf[1024]; int nread, ret; (*linenop)++; rec.name = namebuf; nread = fscanf(filep, "%1024s\t%d\t%d\t%d\t%d\t%d\t%d", rec.name, &rec.pw_min_life, &rec.pw_max_life, &rec.pw_min_length, &rec.pw_min_classes, &rec.pw_history_num, &rec.policy_refcnt); if (nread == EOF) return -1; else if (nread != 7) { fprintf(stderr, gettext("cannot parse policy on line %d (%d read)\n"), *linenop, nread); return 1; } if ((ret = krb5_db_create_policy(kcontext, &rec))) { if (ret && ((ret = krb5_db_put_policy(kcontext, &rec)))) { fprintf(stderr, gettext("cannot create policy on line %d: %s\n"), *linenop, error_message(ret)); return 1; } } if (verbose) fprintf(stderr, gettext("created policy %s\n"), rec.name); return 0; } /* * process_k5beta7_record() - Handle a dump record in krb5b7 format. * * Returns -1 for end of file, 0 for success and 1 for failure. */ static int process_k5beta7_record(fname, kcontext, filep, verbose, linenop) char *fname; krb5_context kcontext; FILE *filep; int verbose; int *linenop; { int nread; char rectype[100]; nread = fscanf(filep, "%100s\t", rectype); if (nread == EOF) return -1; else if (nread != 1) return 1; if (strcmp(rectype, "princ") == 0) process_k5beta6_record(fname, kcontext, filep, verbose, linenop); else if (strcmp(rectype, "policy") == 0) process_k5beta7_policy(fname, kcontext, filep, verbose, linenop); else { fprintf(stderr, gettext("unknown record type \"%s\" on line %d\n"), rectype, *linenop); return 1; } return 0; } /* * process_ov_record() - Handle a dump record in OpenV*Secure 1.0 format. * * Returns -1 for end of file, 0 for success and 1 for failure. */ static int process_ov_record(fname, kcontext, filep, verbose, linenop) char *fname; krb5_context kcontext; FILE *filep; int verbose; int *linenop; { int nread; char rectype[100]; nread = fscanf(filep, "%100s\t", rectype); if (nread == EOF) return -1; else if (nread != 1) return 1; if (strcmp(rectype, "princ") == 0) process_ov_principal(fname, kcontext, filep, verbose, linenop); else if (strcmp(rectype, "policy") == 0) process_k5beta7_policy(fname, kcontext, filep, verbose, linenop); else if (strcmp(rectype, "End") == 0) return -1; else { fprintf(stderr, gettext("unknown record type \"%s\" on line %d\n"), rectype, *linenop); return 1; } return 0; } /* * restore_dump() - Restore the database from any version dump file. */ static int restore_dump(programname, kcontext, dumpfile, f, verbose, dump) char *programname; krb5_context kcontext; char *dumpfile; FILE *f; int verbose; dump_version *dump; { int error; int lineno; error = 0; lineno = 1; /* * Process the records. */ while (!(error = (*dump->load_record)(dumpfile, kcontext, f, verbose, &lineno))) ; if (error != -1) fprintf(stderr, gettext(err_line_fmt), programname, lineno, dumpfile); else error = 0; return(error); } /* * Usage: load_db [-i] [-old] [-ov] [-b6] [-b7] [-verbose] [-update] [-hash] * filename */ void load_db(argc, argv) int argc; char **argv; { kadm5_config_params newparams; krb5_error_code kret; krb5_context kcontext; FILE *f; extern char *optarg; extern int optind; /* Solaris Kerberos */ #if 0 char *programname; #endif char *dumpfile; char *dbname; char *dbname_tmp; char buf[BUFSIZ]; dump_version *load; int update, verbose; int aindex; bool_t add_update = TRUE; char iheader[MAX_HEADER]; uint32_t caller, last_sno, last_seconds, last_useconds; kdb_log_context *log_ctx; int db_locked = 0; /* * Parse the arguments. */ /* Solaris Kerberos */ #if 0 programname = argv[0]; if (strrchr(programname, (int) '/')) programname = strrchr(argv[0], (int) '/') + 1; #endif dumpfile = (char *) NULL; dbname = global_params.dbname; load = NULL; update = 0; verbose = 0; exit_status = 0; dbname_tmp = (char *) NULL; log_ctx = util_context->kdblog_context; for (aindex = 1; aindex < argc; aindex++) { if (!strcmp(argv[aindex], oldoption)) load = &old_version; else if (!strcmp(argv[aindex], b6option)) load = &beta6_version; else if (!strcmp(argv[aindex], b7option)) load = &beta7_version; else if (!strcmp(argv[aindex], ovoption)) load = &ov_version; else if (!strcmp(argv[aindex], ipropoption)) { if (log_ctx && log_ctx->iproprole) { load = &iprop_version; add_update = FALSE; } else { fprintf(stderr, gettext("Iprop not enabled\n")); exit_status++; return; } } else if (!strcmp(argv[aindex], verboseoption)) verbose = 1; else if (!strcmp(argv[aindex], updateoption)) update = 1; else if (!strcmp(argv[aindex], hashoption)) { if (!add_db_arg("hash=true")) { com_err(progname, ENOMEM, "while parsing command arguments\n"); exit(1); } } else break; } if ((argc - aindex) != 1) { usage(); return; } dumpfile = argv[aindex]; if (!(dbname_tmp = (char *) malloc(strlen(dbname)+ strlen(dump_tmptrail)+1))) { /* Solaris Kerberos */ fprintf(stderr, gettext(no_name_mem_fmt), progname); exit_status++; return; } strcpy(dbname_tmp, dbname); strcat(dbname_tmp, dump_tmptrail); /* * Initialize the Kerberos context and error tables. */ if ((kret = kadm5_init_krb5_context(&kcontext))) { /* Solaris Kerberos */ fprintf(stderr, gettext(ctx_err_fmt), progname); free(dbname_tmp); exit_status++; return; } if( (kret = krb5_set_default_realm(kcontext, util_context->default_realm)) ) { /* Solaris Kerberos */ fprintf(stderr, gettext("%s: Unable to set the default realm\n"), progname); free(dbname_tmp); exit_status++; return; } if (log_ctx && log_ctx->iproprole) kcontext->kdblog_context = (void *)log_ctx; /* * Open the dumpfile */ if (dumpfile) { if ((f = fopen(dumpfile, "r")) == NULL) { /* Solaris Kerberos */ fprintf(stderr, gettext(dfile_err_fmt), progname, dumpfile, error_message(errno)); exit_status++; return; } if ((kret = krb5_lock_file(kcontext, fileno(f), KRB5_LOCKMODE_SHARED))) { /* Solaris Kerberos */ fprintf(stderr, gettext("%s: Cannot lock %s: %s\n"), progname, dumpfile, error_message(errno)); exit_status++; return; } } else f = stdin; /* * Auto-detect dump version if we weren't told, verify if we * were told. */ fgets(buf, sizeof(buf), f); if (load) { /* only check what we know; some headers only contain a prefix */ if (strncmp(buf, load->header, strlen(load->header)) != 0) { /* Solaris Kerberos */ fprintf(stderr, gettext(head_bad_fmt), progname, dumpfile); exit_status++; if (dumpfile) fclose(f); return; } } else { /* perhaps this should be in an array, but so what? */ if (strcmp(buf, old_version.header) == 0) load = &old_version; else if (strcmp(buf, beta6_version.header) == 0) load = &beta6_version; else if (strcmp(buf, beta7_version.header) == 0) load = &beta7_version; else if (strcmp(buf, r1_3_version.header) == 0) load = &r1_3_version; else if (strncmp(buf, ov_version.header, strlen(ov_version.header)) == 0) load = &ov_version; else { /* Solaris Kerberos */ fprintf(stderr, gettext(head_bad_fmt), progname, dumpfile); exit_status++; if (dumpfile) fclose(f); return; } } if (load->updateonly && !update) { /* Solaris Kerberos */ fprintf(stderr, gettext("%s: dump version %s can only " "be loaded with the -update flag\n"), progname, load->name); exit_status++; return; } /* * Cons up params for the new databases. If we are not in update * mode, we create an alternate database and then promote it to * be the live db. */ newparams = global_params; if (! update) { newparams.mask |= KADM5_CONFIG_DBNAME; newparams.dbname = dbname_tmp; if ((kret = kadm5_get_config_params(kcontext, 1, &newparams, &newparams))) { /* Solaris Kerberos */ com_err(progname, kret, gettext("while retreiving new " "configuration parameters")); exit_status++; return; } if (!add_db_arg("temporary")) { com_err(progname, ENOMEM, "computing parameters for database"); exit(1); } } /* * If not an update restoration, create the database. otherwise open */ if (!update) { if((kret = krb5_db_create(kcontext, db5util_db_args))) { const char *emsg = krb5_get_error_message(kcontext, kret); /* * See if something (like DAL KDB plugin) has set a specific error * message and use that otherwise use default. */ if (emsg != NULL) { /* Solaris Kerberos */ fprintf(stderr, "%s: %s\n", progname, emsg); krb5_free_error_message (kcontext, emsg); } else { /* Solaris Kerberos */ fprintf(stderr, dbcreaterr_fmt, progname, dbname, error_message(kret)); } exit_status++; kadm5_free_config_params(kcontext, &newparams); if (dumpfile) fclose(f); return; } } else { /* * Initialize the database. */ if ((kret = krb5_db_open(kcontext, db5util_db_args, KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_ADMIN))) { const char *emsg = krb5_get_error_message(kcontext, kret); /* * See if something (like DAL KDB plugin) has set a specific * error message and use that otherwise use default. */ if (emsg != NULL) { /* Solaris Kerberos */ fprintf(stderr, "%s: %s\n", progname, emsg); krb5_free_error_message (kcontext, emsg); } else { /* Solaris Kerberos */ fprintf(stderr, dbinit_err_fmt, progname, error_message(kret)); } exit_status++; goto error; } } /* * If an update restoration, make sure the db is left unusable if * the update fails. */ if ((kret = krb5_db_lock(kcontext, update?KRB5_DB_LOCKMODE_PERMANENT: KRB5_DB_LOCKMODE_EXCLUSIVE))) { /* * Ignore a not supported error since there is nothing to do about it * anyway. */ if (kret != KRB5_PLUGIN_OP_NOTSUPP) { /* Solaris Kerberos */ fprintf(stderr, gettext("%s: %s while permanently locking database\n"), progname, error_message(kret)); exit_status++; goto error; } } else { db_locked = 1; } if (log_ctx && log_ctx->iproprole) { if (add_update) caller = FKCOMMAND; else caller = FKPROPD; if (ulog_map(kcontext, &global_params, caller)) { /* Solaris Kerberos */ fprintf(stderr, gettext("%s: Could not map log\n"), progname); exit_status++; goto error; } /* * We don't want to take out the ulog out from underneath * kadmind so we reinit the header log. * * We also don't want to add to the update log since we * are doing a whole sale replace of the db, because: * we could easily exceed # of update entries * we could implicity delete db entries during a replace * no advantage in incr updates when entire db is replaced */ if (!update) { memset(log_ctx->ulog, 0, sizeof (kdb_hlog_t)); log_ctx->ulog->kdb_hmagic = KDB_HMAGIC; log_ctx->ulog->db_version_num = KDB_VERSION; log_ctx->ulog->kdb_state = KDB_STABLE; log_ctx->ulog->kdb_block = ULOG_BLOCK; log_ctx->iproprole = IPROP_NULL; if (!add_update) { sscanf(buf, "%s %u %u %u", iheader, &last_sno, &last_seconds, &last_useconds); log_ctx->ulog->kdb_last_sno = last_sno; log_ctx->ulog->kdb_last_time.seconds = last_seconds; log_ctx->ulog->kdb_last_time.useconds = last_useconds; } } } /* Solaris Kerberos */ if (restore_dump(progname, kcontext, (dumpfile) ? dumpfile : stdin_name, f, verbose, load)) { /* Solaris Kerberos */ fprintf(stderr, gettext(restfail_fmt), progname, load->name); exit_status++; } if (!update && load->create_kadm5 && ((kret = kadm5_create_magic_princs(&newparams, kcontext)))) { /* error message printed by create_magic_princs */ exit_status++; } if (db_locked && (kret = krb5_db_unlock(kcontext))) { /* change this error? */ /* Solaris Kerberos */ fprintf(stderr, gettext(dbunlockerr_fmt), progname, dbname, error_message(kret)); exit_status++; } #if 0 if ((kret = krb5_db_fini(kcontext))) { /* Solaris Kerberos */ fprintf(stderr, gettext(close_err_fmt), progname, error_message(kret)); exit_status++; } #endif /* close policy db below */ if (exit_status == 0 && !update) { kret = krb5_db_promote(kcontext, db5util_db_args); /* * Ignore a not supported error since there is nothing to do about it * anyway. */ if (kret != 0 && kret != KRB5_PLUGIN_OP_NOTSUPP) { /* Solaris Kerberos */ fprintf(stderr, gettext("%s: cannot make newly loaded database live (%s)\n"), progname, error_message(kret)); exit_status++; } } error: /* * If not an update: if there was an error, destroy the temp database, * otherwise rename it into place. * * If an update: if there was no error, unlock the database. */ if (!update) { if (exit_status) { kret = krb5_db_destroy(kcontext, db5util_db_args); /* * Ignore a not supported error since there is nothing to do about * it anyway. */ if (kret != 0 && kret != KRB5_PLUGIN_OP_NOTSUPP) { /* Solaris Kerberos */ fprintf(stderr, gettext(dbdelerr_fmt), progname, dbname, error_message(kret)); exit_status++; } } } if (dumpfile) { (void) krb5_lock_file(kcontext, fileno(f), KRB5_LOCKMODE_UNLOCK); fclose(f); } if (dbname_tmp) free(dbname_tmp); krb5_free_context(kcontext); } /* * 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 * */ /* * import_err.h: * This file is automatically generated; please do not edit it. */ #include #define IMPORT_NO_ERR (37349888L) #define IMPORT_BAD_FILE (37349889L) #define IMPORT_BAD_TOKEN (37349890L) #define IMPORT_BAD_VERSION (37349891L) #define IMPORT_BAD_RECORD (37349892L) #define IMPORT_BAD_FOOTER (37349893L) #define IMPORT_FAILED (37349894L) #define IMPORT_COUNT_MESSAGE (37349895L) #define IMPORT_MISMATCH_COUNT (37349896L) #define IMPORT_UNK_OPTION (37349897L) #define IMPORT_WARN_DB (37349898L) #define IMPORT_RENAME_FAILED (37349899L) #define IMPORT_EXTRA_DATA (37349900L) #define IMPORT_CONFIRM (37349901L) #define IMPORT_OPEN_DUMP (37349902L) #define IMPORT_IMPORT (37349903L) #define IMPORT_TTY (37349904L) #define IMPORT_RENAME_OPEN (37349905L) #define IMPORT_RENAME_LOCK (37349906L) #define IMPORT_RENAME_UNLOCK (37349907L) #define IMPORT_RENAME_CLOSE (37349908L) #define IMPORT_SINGLE_RECORD (37349909L) #define IMPORT_PLURAL_RECORDS (37349910L) #define IMPORT_GET_PARAMS (37349911L) #define ERROR_TABLE_BASE_imp (37349888L) extern const struct error_table et_imp_error_table; #if !defined(_WIN32) /* for compatibility with older versions... */ extern void initialize_imp_error_table (void) /*@modifies internalState@*/; #else #define initialize_imp_error_table() #endif #if !defined(_WIN32) #define init_imp_err_tbl initialize_imp_error_table #define imp_err_base ERROR_TABLE_BASE_imp #endif /* * Please do not edit this file. * It was generated using rpcgen. */ #ifndef _IPROP_H_RPCGEN #define _IPROP_H_RPCGEN #include typedef struct { u_int utf8str_t_len; char *utf8str_t_val; } utf8str_t; typedef uint32_t kdb_sno_t; struct kdbe_time_t { uint32_t seconds; uint32_t useconds; }; typedef struct kdbe_time_t kdbe_time_t; struct kdbe_key_t { int32_t k_ver; int32_t k_kvno; struct { u_int k_enctype_len; int32_t *k_enctype_val; } k_enctype; struct { u_int k_contents_len; utf8str_t *k_contents_val; } k_contents; }; typedef struct kdbe_key_t kdbe_key_t; struct kdbe_data_t { int32_t k_magic; utf8str_t k_data; }; typedef struct kdbe_data_t kdbe_data_t; struct kdbe_princ_t { utf8str_t k_realm; struct { u_int k_components_len; kdbe_data_t *k_components_val; } k_components; int32_t k_nametype; }; typedef struct kdbe_princ_t kdbe_princ_t; struct kdbe_tl_t { int16_t tl_type; struct { u_int tl_data_len; char *tl_data_val; } tl_data; }; typedef struct kdbe_tl_t kdbe_tl_t; typedef struct { u_int kdbe_pw_hist_t_len; kdbe_key_t *kdbe_pw_hist_t_val; } kdbe_pw_hist_t; enum kdbe_attr_type_t { AT_ATTRFLAGS = 0, AT_MAX_LIFE = 1, AT_MAX_RENEW_LIFE = 2, AT_EXP = 3, AT_PW_EXP = 4, AT_LAST_SUCCESS = 5, AT_LAST_FAILED = 6, AT_FAIL_AUTH_COUNT = 7, AT_PRINC = 8, AT_KEYDATA = 9, AT_TL_DATA = 10, AT_LEN = 11, AT_MOD_PRINC = 12, AT_MOD_TIME = 13, AT_MOD_WHERE = 14, AT_PW_LAST_CHANGE = 15, AT_PW_POLICY = 16, AT_PW_POLICY_SWITCH = 17, AT_PW_HIST_KVNO = 18, AT_PW_HIST = 19 }; typedef enum kdbe_attr_type_t kdbe_attr_type_t; struct kdbe_val_t { kdbe_attr_type_t av_type; union { uint32_t av_attrflags; uint32_t av_max_life; uint32_t av_max_renew_life; uint32_t av_exp; uint32_t av_pw_exp; uint32_t av_last_success; uint32_t av_last_failed; uint32_t av_fail_auth_count; kdbe_princ_t av_princ; struct { u_int av_keydata_len; kdbe_key_t *av_keydata_val; } av_keydata; struct { u_int av_tldata_len; kdbe_tl_t *av_tldata_val; } av_tldata; int16_t av_len; uint32_t av_pw_last_change; kdbe_princ_t av_mod_princ; uint32_t av_mod_time; utf8str_t av_mod_where; utf8str_t av_pw_policy; bool_t av_pw_policy_switch; uint32_t av_pw_hist_kvno; struct { u_int av_pw_hist_len; kdbe_pw_hist_t *av_pw_hist_val; } av_pw_hist; struct { u_int av_extension_len; char *av_extension_val; } av_extension; } kdbe_val_t_u; }; typedef struct kdbe_val_t kdbe_val_t; typedef struct { u_int kdbe_t_len; kdbe_val_t *kdbe_t_val; } kdbe_t; struct kdb_incr_update_t { utf8str_t kdb_princ_name; kdb_sno_t kdb_entry_sno; kdbe_time_t kdb_time; kdbe_t kdb_update; bool_t kdb_deleted; bool_t kdb_commit; struct { u_int kdb_kdcs_seen_by_len; utf8str_t *kdb_kdcs_seen_by_val; } kdb_kdcs_seen_by; struct { u_int kdb_futures_len; char *kdb_futures_val; } kdb_futures; }; typedef struct kdb_incr_update_t kdb_incr_update_t; typedef struct { u_int kdb_ulog_t_len; kdb_incr_update_t *kdb_ulog_t_val; } kdb_ulog_t; enum update_status_t { UPDATE_OK = 0, UPDATE_ERROR = 1, UPDATE_FULL_RESYNC_NEEDED = 2, UPDATE_BUSY = 3, UPDATE_NIL = 4, UPDATE_PERM_DENIED = 5 }; typedef enum update_status_t update_status_t; struct kdb_last_t { kdb_sno_t last_sno; kdbe_time_t last_time; }; typedef struct kdb_last_t kdb_last_t; struct kdb_incr_result_t { kdb_last_t lastentry; kdb_ulog_t updates; update_status_t ret; }; typedef struct kdb_incr_result_t kdb_incr_result_t; struct kdb_fullresync_result_t { kdb_last_t lastentry; update_status_t ret; }; typedef struct kdb_fullresync_result_t kdb_fullresync_result_t; #define KRB5_IPROP_PROG 100423 #define KRB5_IPROP_VERS 1 #define IPROP_NULL 0 extern void * iprop_null_1(); #define IPROP_GET_UPDATES 1 extern kdb_incr_result_t * iprop_get_updates_1(); #define IPROP_FULL_RESYNC 2 extern kdb_fullresync_result_t * iprop_full_resync_1(); extern int krb5_iprop_prog_1_freeresult(); /* the xdr functions */ extern bool_t xdr_utf8str_t(); extern bool_t xdr_kdb_sno_t(); extern bool_t xdr_kdbe_time_t(); extern bool_t xdr_kdbe_key_t(); extern bool_t xdr_kdbe_data_t(); extern bool_t xdr_kdbe_princ_t(); extern bool_t xdr_kdbe_tl_t(); extern bool_t xdr_kdbe_pw_hist_t(); extern bool_t xdr_kdbe_attr_type_t(); extern bool_t xdr_kdbe_val_t(); extern bool_t xdr_kdbe_t(); extern bool_t xdr_kdb_incr_update_t(); extern bool_t xdr_kdb_ulog_t(); extern bool_t xdr_update_status_t(); extern bool_t xdr_kdb_last_t(); extern bool_t xdr_kdb_incr_result_t(); extern bool_t xdr_kdb_fullresync_result_t(); #endif /* !_IPROP_H_RPCGEN */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. * * $Id: kadm5_create.c,v 1.6 1998/10/30 02:52:37 marc Exp $ * $Source: /cvs/krbdev/krb5/src/kadmin/dbutil/kadm5_create.c,v $ */ /* * Copyright (C) 1998 by the FundsXpress, INC. * * 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 FundsXpress. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. FundsXpress makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #include "string_table.h" #include #include #include #include #include #include #include #include #include #include "kdb5_util.h" #include int add_admin_old_princ(void *handle, krb5_context context, char *name, char *realm, int attrs, int lifetime); int add_admin_sname_princ(void *handle, krb5_context context, char *sname, int attrs, int lifetime); static int add_admin_princ(void *handle, krb5_context context, krb5_principal principal, int attrs, int lifetime); static int add_admin_princs(void *handle, krb5_context context, char *realm); #define ERR 1 #define OK 0 #define ADMIN_LIFETIME 60*60*3 /* 3 hours */ #define CHANGEPW_LIFETIME 60*5 /* 5 minutes */ extern char *progname; /* * Function: kadm5_create * * Purpose: create admin principals in KDC database * * Arguments: params (r) configuration parameters to use * * Effects: Creates KADM5_ADMIN_SERVICE and KADM5_CHANGEPW_SERVICE * principals in the KDC database and sets their attributes * appropriately. */ int kadm5_create(kadm5_config_params *params) { int retval; krb5_context context; kadm5_config_params lparams; if ((retval = kadm5_init_krb5_context(&context))) exit(ERR); (void) memset(&lparams, 0, sizeof (kadm5_config_params)); /* * The lock file has to exist before calling kadm5_init, but * params->admin_lockfile may not be set yet... */ if ((retval = kadm5_get_config_params(context, 1, params, &lparams))) { com_err(progname, retval, gettext("while looking up the Kerberos configuration")); return 1; } retval = kadm5_create_magic_princs(&lparams, context); kadm5_free_config_params(context, &lparams); krb5_free_context(context); return retval; } int kadm5_create_magic_princs(kadm5_config_params *params, krb5_context context) { int retval; void *handle; retval = krb5_klog_init(context, "admin_server", progname, 0); if (retval) return retval; if ((retval = kadm5_init(progname, NULL, NULL, params, KADM5_STRUCT_VERSION, KADM5_API_VERSION_2, db5util_db_args, &handle))) { com_err(progname, retval, gettext("while initializing the Kerberos admin interface")); return retval; } retval = add_admin_princs(handle, context, params->realm); kadm5_destroy(handle); krb5_klog_close(context); return retval; } /* * Function: build_name_with_realm * * Purpose: concatenate a name and a realm to form a krb5 name * * Arguments: * * name (input) the name * realm (input) the realm * * Returns: * * pointer to name@realm, in allocated memory, or NULL if it * cannot be allocated * * Requires: both strings are null-terminated */ static char *build_name_with_realm(char *name, char *realm) { char *n; n = (char *) malloc(strlen(name) + strlen(realm) + 2); sprintf(n, "%s@%s", name, realm); return n; } /* * Function: add_admin_princs * * Purpose: create admin principals * * Arguments: * * rseed (input) random seed * realm (input) realm, or NULL for default realm * (output) status, 0 for success, 1 for serious error * * Requires: * * Effects: * * add_admin_princs creates KADM5_ADMIN_SERVICE, * KADM5_CHANGEPW_SERVICE. If any of these exist a message is * printed. If any of these existing principal do not have the proper * attributes, a warning message is printed. */ static int add_admin_princs(void *handle, krb5_context context, char *realm) { krb5_error_code ret = 0; /* * Solaris Kerberos: * The kadmin/admin principal is unused on Solaris. This principal is used * in AUTH_GSSAPI but Solaris doesn't support AUTH_GSSAPI. RPCSEC_GSS can only * be used with host-based principals. * */ #if 0 if ((ret = add_admin_old_princ(handle, context, KADM5_ADMIN_SERVICE, realm, KRB5_KDB_DISALLOW_TGT_BASED, ADMIN_LIFETIME))) goto clean_and_exit; #endif if ((ret = add_admin_old_princ(handle, context, KADM5_CHANGEPW_SERVICE, realm, KRB5_KDB_DISALLOW_TGT_BASED | KRB5_KDB_PWCHANGE_SERVICE, CHANGEPW_LIFETIME))) goto clean_and_exit; if ((ret = add_admin_sname_princ(handle, context, KADM5_ADMIN_HOST_SERVICE, KRB5_KDB_DISALLOW_TGT_BASED, ADMIN_LIFETIME))) goto clean_and_exit; if ((ret = add_admin_sname_princ(handle, context, KADM5_CHANGEPW_HOST_SERVICE, KRB5_KDB_DISALLOW_TGT_BASED | KRB5_KDB_PWCHANGE_SERVICE, ADMIN_LIFETIME))) goto clean_and_exit; if ((ret = add_admin_sname_princ(handle, context, KADM5_KIPROP_HOST_SERVICE, KRB5_KDB_DISALLOW_TGT_BASED, ADMIN_LIFETIME))) goto clean_and_exit; clean_and_exit: return ret; } /* * Function: add_admin_princ * * Arguments: * * creator (r) principal to use as "mod_by" * rseed (r) seed for random key generator * principal (r) kerberos principal to add * attrs (r) principal's attributes * lifetime (r) principal's max life, or 0 * not_unique (r) error message for multiple entries, never used * exists (r) warning message for principal exists * wrong_attrs (r) warning message for wrong attributes * * Returns: * * OK on success * ERR on serious errors * * Effects: * * If the principal is not unique, not_unique is printed (but this * never happens). If the principal exists, then exists is printed * and if the principals attributes != attrs, wrong_attrs is printed. * Otherwise, the principal is created with mod_by creator and * attributes attrs and max life of lifetime (if not zero). */ static int add_admin_princ(void *handle, krb5_context context, krb5_principal principal, int attrs, int lifetime) { char *fullname; krb5_error_code ret; kadm5_principal_ent_rec ent; memset(&ent, 0, sizeof(ent)); if (krb5_unparse_name(context, principal, &fullname)) return ERR; ent.principal = principal; ent.max_life = lifetime; ent.attributes = attrs | KRB5_KDB_DISALLOW_ALL_TIX; ret = kadm5_create_principal(handle, &ent, (KADM5_PRINCIPAL | KADM5_MAX_LIFE | KADM5_ATTRIBUTES), "to-be-random"); if (ret) { if (ret != KADM5_DUP) { com_err(progname, ret, gettext(str_PUT_PRINC), fullname); krb5_free_principal(context, ent.principal); free(fullname); return ERR; } } else { /* only randomize key if we created the principal */ /* * Solaris Kerberos: * Create kadmind principals with keys for all supported encryption types. * Follows a similar pattern to add_principal() in keytab.c. */ krb5_enctype *tmpenc, *enctype = NULL; krb5_key_salt_tuple *keysalt; int num_ks, i; krb5_int32 normalsalttype; ret = krb5_get_permitted_enctypes(context, &enctype); if (ret || *enctype == 0) { com_err(progname, ret, gettext("while getting list of permitted encryption types")); krb5_free_principal(context, ent.principal); free(fullname); return ERR; } /* Count the number of enc types */ for (tmpenc = enctype, num_ks = 0; *tmpenc; tmpenc++) num_ks++; keysalt = malloc (sizeof (krb5_key_salt_tuple) * num_ks); if (keysalt == NULL) { com_err(progname, ENOMEM, gettext("while generating list of key salt tuples")); krb5_free_ktypes(context, enctype); krb5_free_principal(context, ent.principal); free(fullname); return ERR; } ret = krb5_string_to_salttype("normal", &normalsalttype); if (ret) { com_err(progname, ret, gettext("while converting \"normal\" to a salttype")); free(keysalt); krb5_free_ktypes(context, enctype); krb5_free_principal(context, ent.principal); free(fullname); return ERR; } /* Only create keys with "normal" salttype */ for (i = 0; i < num_ks; i++) { keysalt[i].ks_enctype = enctype[i]; keysalt[i].ks_salttype = normalsalttype; } ret = kadm5_randkey_principal_3(handle, ent.principal, FALSE, num_ks, keysalt, NULL, NULL); free(keysalt); krb5_free_ktypes (context, enctype); if (ret) { com_err(progname, ret, gettext(str_RANDOM_KEY), fullname); krb5_free_principal(context, ent.principal); free(fullname); return ERR; } ent.attributes = attrs; ret = kadm5_modify_principal(handle, &ent, KADM5_ATTRIBUTES); if (ret) { com_err(progname, ret, gettext(str_PUT_PRINC), fullname); krb5_free_principal(context, ent.principal); free(fullname); return ERR; } } krb5_free_principal(context, ent.principal); free(fullname); return OK; } int add_admin_old_princ(void *handle, krb5_context context, char *name, char *realm, int attrs, int lifetime) { char *fullname; krb5_error_code ret; krb5_principal principal; fullname = build_name_with_realm(name, realm); if (ret = krb5_parse_name(context, fullname, &principal)) { com_err(progname, ret, gettext(str_PARSE_NAME)); return (ERR); } return (add_admin_princ(handle, context, principal, attrs, lifetime)); } int add_admin_sname_princ(void *handle, krb5_context context, char *sname, int attrs, int lifetime) { krb5_error_code ret; krb5_principal principal; if (ret = krb5_sname_to_principal(context, NULL, sname, KRB5_NT_SRV_HST, &principal)) { com_err(progname, ret, gettext("Could not get host based " "service name for %s principal\n"), sname); return (ERR); } return (add_admin_princ(handle, context, principal, attrs, lifetime)); } /* * Copyright 2008 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 * */ /* * admin/create/kdb5_create.c * * Copyright 1990,1991 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. * * * Generate (from scratch) a Kerberos KDC database. */ /* * Yes, I know this is a hack, but we need admin.h without including the * rpc.h header. Additionally, our rpc.h header brings in * a des.h header which causes other problems. */ #define _RPC_RPC_H #include #include #include #include #include #include #include #include #include "kdb5_util.h" enum ap_op { NULL_KEY, /* setup null keys */ MASTER_KEY, /* use master key as new key */ TGT_KEY /* special handling for tgt key */ }; krb5_key_salt_tuple def_kslist = { ENCTYPE_DES_CBC_CRC, KRB5_KDB_SALTTYPE_NORMAL }; struct realm_info { krb5_deltat max_life; krb5_deltat max_rlife; krb5_timestamp expiration; krb5_flags flags; krb5_keyblock *key; krb5_int32 nkslist; krb5_key_salt_tuple *kslist; } rblock = { /* XXX */ KRB5_KDB_MAX_LIFE, KRB5_KDB_MAX_RLIFE, KRB5_KDB_EXPIRATION, KRB5_KDB_DEF_FLAGS, (krb5_keyblock *) NULL, 1, &def_kslist }; struct iterate_args { krb5_context ctx; struct realm_info *rblock; krb5_db_entry *dbentp; }; static krb5_error_code add_principal (krb5_context, krb5_principal, enum ap_op, struct realm_info *, krb5_keyblock *); /* * Steps in creating a database: * * 1) use the db calls to open/create a new database * * 2) get a realm name for the new db * * 3) get a master password for the new db; convert to an encryption key. * * 4) create various required entries in the database * * 5) close & exit */ extern krb5_principal master_princ; krb5_data tgt_princ_entries[] = { {0, KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME}, {0, 0, 0} }; krb5_data db_creator_entries[] = { {0, sizeof("db_creation")-1, "db_creation"} }; /* XXX knows about contents of krb5_principal, and that tgt names are of form TGT/REALM@REALM */ krb5_principal_data tgt_princ = { 0, /* magic number */ {0, 0, 0}, /* krb5_data realm */ tgt_princ_entries, /* krb5_data *data */ 2, /* int length */ KRB5_NT_SRV_INST /* int type */ }; krb5_principal_data db_create_princ = { 0, /* magic number */ {0, 0, 0}, /* krb5_data realm */ db_creator_entries, /* krb5_data *data */ 1, /* int length */ KRB5_NT_SRV_INST /* int type */ }; extern char *mkey_password; extern char *progname; extern int exit_status; extern kadm5_config_params global_params; extern krb5_context util_context; void kdb5_create(argc, argv) int argc; char *argv[]; { int optchar; krb5_error_code retval; char *mkey_fullname; char *pw_str = 0; unsigned int pw_size = 0; int do_stash = 0; krb5_data pwd, seed; kdb_log_context *log_ctx; krb5_keyblock mkey; krb5_data master_salt = { 0 }; /* Solaris Kerberos */ (void) memset(&mkey, 0, sizeof (mkey)); /* Solaris Kerberos */ #if 0 if (strrchr(argv[0], '/')) argv[0] = strrchr(argv[0], '/')+1; #endif while ((optchar = getopt(argc, argv, "s")) != -1) { switch(optchar) { case 's': do_stash++; break; case 'h': if (!add_db_arg("hash=true")) { com_err(progname, ENOMEM, "while parsing command arguments\n"); exit(1); } break; case '?': default: usage(); return; } } rblock.max_life = global_params.max_life; rblock.max_rlife = global_params.max_rlife; rblock.expiration = global_params.expiration; rblock.flags = global_params.flags; rblock.nkslist = global_params.num_keysalts; rblock.kslist = global_params.keysalts; log_ctx = util_context->kdblog_context; /* SUNW14resync XXX */ #if 0 printf ("Loading random data\n"); retval = krb5_c_random_os_entropy (util_context, 1, NULL); if (retval) { /* Solaris Kerberos */ com_err (progname, retval, "Loading random data"); exit_status++; return; } #endif /* assemble & parse the master key name */ if ((retval = krb5_db_setup_mkey_name(util_context, global_params.mkey_name, global_params.realm, &mkey_fullname, &master_princ))) { /* Solaris Kerberos */ com_err(progname, retval, gettext("while setting up master key name")); exit_status++; return; } krb5_princ_set_realm_data(util_context, &db_create_princ, global_params.realm); krb5_princ_set_realm_length(util_context, &db_create_princ, strlen(global_params.realm)); krb5_princ_set_realm_data(util_context, &tgt_princ, global_params.realm); krb5_princ_set_realm_length(util_context, &tgt_princ, strlen(global_params.realm)); krb5_princ_component(util_context, &tgt_princ,1)->data = global_params.realm; krb5_princ_component(util_context, &tgt_princ,1)->length = strlen(global_params.realm); printf(gettext("Initializing database '%s' for realm '%s',\n" "master key name '%s'\n"), global_params.dbname, global_params.realm, mkey_fullname); if (!mkey_password) { printf(gettext("You will be prompted for the " "database Master Password.\n")); printf(gettext("It is important that you NOT FORGET this password.\n")); fflush(stdout); pw_size = 1024; pw_str = malloc(pw_size); retval = krb5_read_password(util_context, gettext("Enter KDC database master key"), gettext("Re-enter KDC database " "master key to verify"), pw_str, &pw_size); if (retval) { /* Solaris Kerberos */ com_err(progname, retval, gettext("while reading master key from keyboard")); exit_status++; return; } mkey_password = pw_str; } pwd.data = mkey_password; pwd.length = strlen(mkey_password); retval = krb5_principal2salt(util_context, master_princ, &master_salt); if (retval) { /* Solaris Kerberos */ com_err(progname, retval, gettext("while calculated master key salt")); exit_status++; goto cleanup; } retval = krb5_c_string_to_key(util_context, global_params.enctype, &pwd, &master_salt, &mkey); if (retval) { /* Solaris Kerberos */ com_err(progname, retval, gettext("while transforming master key from password")); exit_status++; goto cleanup; } retval = krb5_copy_keyblock(util_context, &mkey, &rblock.key); if (retval) { /* Solaris Kerberos */ com_err(progname, retval, gettext("while copying master key")); exit_status++; goto cleanup; } seed.length = mkey.length; seed.data = (char *)mkey.contents; if ((retval = krb5_c_random_seed(util_context, &seed))) { /* Solaris Kerberos */ com_err(progname, retval, gettext("while initializing random key generator")); exit_status++; goto cleanup; } if ((retval = krb5_db_create(util_context, db5util_db_args))) { /* Solaris Kerberos */ com_err(progname, retval, gettext("while creating database '%s'"), global_params.dbname); exit_status++; goto cleanup; } #if 0 /************** Begin IFDEF'ed OUT *******************************/ if (retval = krb5_db_fini(util_context)) { /* Solaris Kerberos */ com_err(progname, retval, gettext("while closing current database")); exit_status++; goto cleanup; } if ((retval = krb5_db_set_name(util_context, global_params.dbname))) { /* Solaris Kerberos */ com_err(progname, retval, gettext("while setting active database to '%s'"), global_params.dbname); exit_status++; goto cleanup; } if ((retval = krb5_db_init(util_context))) { com_err(progname, retval, gettext("while initializing the database '%s'"), global_params.dbname); exit_status++; goto cleanup; } #endif /**************** END IFDEF'ed OUT *******************************/ /* Solaris Kerberos: for iprop */ if (log_ctx && log_ctx->iproprole) { if (retval = ulog_map(util_context, &global_params, FKCOMMAND)) { /* Solaris Kerberos */ com_err(progname, retval, gettext("while creating update log")); exit_status++; goto cleanup; } /* * We're reinitializing the update log in case one already * existed, but this should never happen. */ (void) memset(log_ctx->ulog, 0, sizeof (kdb_hlog_t)); log_ctx->ulog->kdb_hmagic = KDB_HMAGIC; log_ctx->ulog->db_version_num = KDB_VERSION; log_ctx->ulog->kdb_state = KDB_STABLE; log_ctx->ulog->kdb_block = ULOG_BLOCK; /* * Since we're creating a new db we shouldn't worry about * adding the initial principals since any slave might as well * do full resyncs from this newly created db. */ log_ctx->iproprole = IPROP_NULL; } if ((retval = add_principal(util_context, master_princ, MASTER_KEY, &rblock, &mkey)) || (retval = add_principal(util_context, &tgt_princ, TGT_KEY, &rblock, &mkey))) { (void) krb5_db_fini(util_context); /* Solaris Kerberos */ com_err(progname, retval, gettext("while adding entries to the database")); exit_status++; goto cleanup; } /* * Always stash the master key so kadm5_create does not prompt for * it; delete the file below if it was not requested. DO NOT EXIT * BEFORE DELETING THE KEYFILE if do_stash is not set. */ retval = krb5_db_store_master_key(util_context, global_params.stash_file, master_princ, &mkey, mkey_password); if (retval) { /* Solaris Kerberos */ com_err(progname, errno, gettext("while storing key")); printf(gettext("Warning: couldn't stash master key.\n")); } if (pw_str) memset(pw_str, 0, pw_size); if (kadm5_create(&global_params)) { if (!do_stash) unlink(global_params.stash_file); exit_status++; goto cleanup; } if (!do_stash) unlink(global_params.stash_file); /* Solaris Kerberos: deal with master_keyblock in better way */ cleanup: if (pw_str) { if (mkey_password == pw_str) mkey_password = NULL; free(pw_str); } if (master_salt.data) free(master_salt.data); krb5_free_keyblock(util_context, rblock.key); krb5_free_keyblock_contents(util_context, &mkey); (void) krb5_db_fini(util_context); return; } static krb5_error_code tgt_keysalt_iterate(ksent, ptr) krb5_key_salt_tuple *ksent; krb5_pointer ptr; { krb5_context context; krb5_error_code kret; struct iterate_args *iargs; krb5_keyblock key; krb5_int32 ind; krb5_data pwd; iargs = (struct iterate_args *) ptr; kret = 0; context = iargs->ctx; /* * Convert the master key password into a key for this particular * encryption system. */ pwd.data = mkey_password; pwd.length = strlen(mkey_password); kret = krb5_c_random_seed(context, &pwd); if (kret) return kret; if (!(kret = krb5_dbe_create_key_data(iargs->ctx, iargs->dbentp))) { ind = iargs->dbentp->n_key_data-1; if (!(kret = krb5_c_make_random_key(context, ksent->ks_enctype, &key))) { kret = krb5_dbekd_encrypt_key_data(context, iargs->rblock->key, &key, NULL, 1, &iargs->dbentp->key_data[ind]); krb5_free_keyblock_contents(context, &key); } } return(kret); } static krb5_error_code add_principal(context, princ, op, pblock, mkey) krb5_context context; krb5_principal princ; enum ap_op op; struct realm_info *pblock; krb5_keyblock *mkey; { krb5_error_code retval; krb5_db_entry entry; krb5_timestamp now; struct iterate_args iargs; int nentries = 1; memset((char *) &entry, 0, sizeof(entry)); entry.len = KRB5_KDB_V1_BASE_LENGTH; entry.attributes = pblock->flags; entry.max_life = pblock->max_life; entry.max_renewable_life = pblock->max_rlife; entry.expiration = pblock->expiration; if ((retval = krb5_copy_principal(context, princ, &entry.princ))) goto error_out; if ((retval = krb5_timeofday(context, &now))) goto error_out; if ((retval = krb5_dbe_update_mod_princ_data(context, &entry, now, &db_create_princ))) goto error_out; switch (op) { case MASTER_KEY: if ((entry.key_data=(krb5_key_data*)malloc(sizeof(krb5_key_data))) == NULL) goto error_out; memset((char *) entry.key_data, 0, sizeof(krb5_key_data)); entry.n_key_data = 1; entry.attributes |= KRB5_KDB_DISALLOW_ALL_TIX; if ((retval = krb5_dbekd_encrypt_key_data(context, pblock->key, mkey, NULL, 1, entry.key_data))) goto error_out; break; case TGT_KEY: iargs.ctx = context; iargs.rblock = pblock; iargs.dbentp = &entry; /* * Iterate through the key/salt list, ignoring salt types. */ if ((retval = krb5_keysalt_iterate(pblock->kslist, pblock->nkslist, 1, tgt_keysalt_iterate, (krb5_pointer) &iargs))) return retval; break; case NULL_KEY: return EOPNOTSUPP; default: break; } entry.mask = (KADM5_KEY_DATA | KADM5_PRINCIPAL | KADM5_ATTRIBUTES | KADM5_MAX_LIFE | KADM5_MAX_RLIFE | KADM5_TL_DATA | KADM5_PRINC_EXPIRE_TIME); retval = krb5_db_put_principal(context, &entry, &nentries); error_out:; krb5_db_free_principal(context, &entry, 1); return retval; } /* * Copyright 2008 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 * */ /* * admin/destroy/kdb5_destroy.c * * Copyright 1990 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. * * * kdb_dest(roy): destroy the named database. * * This version knows about DBM format databases. */ #include "k5-int.h" #include #include "com_err.h" #include #include #include #include "kdb5_util.h" extern int exit_status; extern krb5_boolean dbactive; extern kadm5_config_params global_params; void kdb5_destroy(argc, argv) int argc; char *argv[]; { extern char *optarg; extern int optind; int optchar; char *dbname; char buf[5]; krb5_error_code retval1; krb5_context context; int force = 0; char ufilename[MAX_FILENAME]; retval1 = kadm5_init_krb5_context(&context); if( retval1 ) { /* Solaris Kerberos */ com_err(progname, retval1, "while initializing krb5_context"); exit(1); } if ((retval1 = krb5_set_default_realm(context, util_context->default_realm))) { /* Solaris Kerberos */ com_err(progname, retval1, "while setting default realm name"); exit(1); } /* Solaris Kerberos */ #if 0 if (strrchr(argv[0], '/')) argv[0] = strrchr(argv[0], '/')+1; #endif dbname = global_params.dbname; optind = 1; while ((optchar = getopt(argc, argv, "f")) != -1) { switch(optchar) { case 'f': force++; break; case '?': default: usage(); return; /*NOTREACHED*/ } } if (!force) { printf(gettext("Deleting KDC database stored in '%s', " "are you sure?\n"), dbname); printf(gettext("(type 'yes' or 'y' to confirm)? ")); if (fgets(buf, sizeof(buf), stdin) == NULL) { exit_status++; return; } if ((strncmp(buf, gettext("yes\n"), strlen(gettext("yes\n"))) != 0) && (strncmp(buf, gettext("y\n"), strlen(gettext("y\n"))) != 0)) { printf(gettext("database not deleted !! '%s'...\n"), dbname); exit_status++; return; } printf(gettext("OK, deleting database '%s'...\n"), dbname); } retval1 = krb5_db_destroy(context, db5util_db_args); /* check for a stash file and delete it if necessary */ if (global_params.stash_file == NULL) { char stash[MAXPATHLEN+1]; extern krb5_principal master_princ; krb5_data *realm = krb5_princ_realm(context, master_princ); (void) strlcpy(stash, DEFAULT_KEYFILE_STUB, sizeof (stash)); /* * realm->data is not necessarily NULL terminated so be * careful how much data is copied here. Don't overrun * the "stash" buffer and dont overrun the realm->data buffer, * copy the smaller of the 2 lengths. */ (void) strncat(stash, realm->data, (realm->length < (MAXPATHLEN-strlen(stash)) ? realm->length : MAXPATHLEN-strlen(stash))); global_params.stash_file = (char *)strdup(stash); } if (!access(global_params.stash_file, F_OK)) (void)unlink(global_params.stash_file); if (retval1) { /* Solaris Kerberos */ com_err(progname, retval1, gettext("deleting database '%s'"), dbname); exit_status++; return; } if (global_params.iprop_enabled) { if (strlcpy(ufilename, dbname, MAX_FILENAME) >= MAX_FILENAME) { exit_status++; return; } if (strlcat(ufilename, ".ulog", MAX_FILENAME) >= MAX_FILENAME) { exit_status++; return; } (void) unlink(ufilename); } dbactive = FALSE; printf(gettext("** Database '%s' destroyed.\n"), dbname); return; } /* * Copyright 2008 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 * */ /* * admin/stash/kdb5_stash.c * * Copyright 1990 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. * * * Store the master database key in a file. */ /* * Copyright (C) 1998 by the FundsXpress, INC. * * 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 FundsXpress. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. FundsXpress makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #include "k5-int.h" #include #include "com_err.h" #include #include #include #include "kdb5_util.h" extern krb5_principal master_princ; extern kadm5_config_params global_params; extern int exit_status; void kdb5_stash(argc, argv) int argc; char *argv[]; { extern char *optarg; extern int optind; int optchar; krb5_error_code retval; char *dbname = (char *) NULL; char *realm = 0; char *mkey_name = 0; char *mkey_fullname; char *keyfile = 0; krb5_context context; krb5_keyblock mkey; /* Solaris Kerberos */ #if 0 if (strrchr(argv[0], '/')) argv[0] = strrchr(argv[0], '/')+1; #endif retval = kadm5_init_krb5_context(&context); if( retval ) { /* Solaris Kerberos */ com_err(progname, retval, "while initializing krb5_context"); exit(1); } if ((retval = krb5_set_default_realm(context, util_context->default_realm))) { /* Solaris Kerberos */ com_err(progname, retval, "while setting default realm name"); exit(1); } dbname = global_params.dbname; realm = global_params.realm; mkey_name = global_params.mkey_name; keyfile = global_params.stash_file; optind = 1; while ((optchar = getopt(argc, argv, "f:")) != -1) { switch(optchar) { case 'f': keyfile = optarg; break; case '?': default: usage(); return; } } if (!krb5_c_valid_enctype(global_params.enctype)) { char tmp[32]; if (krb5_enctype_to_string(global_params.enctype, tmp, sizeof (tmp))) /* Solaris Kerberos */ com_err(progname, KRB5_PROG_KEYTYPE_NOSUPP, gettext("while setting up enctype %d"), global_params.enctype); else { /* Solaris Kerberos */ com_err(progname, KRB5_PROG_KEYTYPE_NOSUPP, tmp); } exit_status++; return; } /* assemble & parse the master key name */ retval = krb5_db_setup_mkey_name(context, mkey_name, realm, &mkey_fullname, &master_princ); if (retval) { /* Solaris Kerberos */ com_err(progname, retval, gettext("while setting up master key name")); exit_status++; return; } retval = krb5_db_open(context, db5util_db_args, KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_OTHER); if (retval) { /* Solaris Kerberos */ com_err(progname, retval, gettext("while initializing the database '%s'"), dbname); exit_status++; return; } /* TRUE here means read the keyboard, but only once */ retval = krb5_db_fetch_mkey(context, master_princ, global_params.enctype, TRUE, FALSE, (char *) NULL, 0, &mkey); if (retval) { /* Solaris Kerberos */ com_err(progname, retval, gettext("while reading master key")); (void) krb5_db_fini(context); exit_status++; return; } retval = krb5_db_verify_master_key(context, master_princ, &mkey); if (retval) { /* Solaris Kerberos */ com_err(progname, retval, gettext("while verifying master key")); krb5_free_keyblock_contents(context, &mkey); (void) krb5_db_fini(context); exit_status++; return; } retval = krb5_db_store_master_key(context, keyfile, master_princ, &mkey, NULL); if (retval) { /* Solaris Kerberos */ com_err(progname, errno, gettext("while storing key")); krb5_free_keyblock_contents(context, &mkey); (void) krb5_db_fini(context); exit_status++; return; } krb5_free_keyblock_contents(context, &mkey); retval = krb5_db_fini(context); if (retval) { /* Solaris Kerberos */ com_err(progname, retval, gettext("closing database '%s'"), dbname); exit_status++; return; } krb5_free_context(context); exit_status = 0; return; } /* * Copyright 2008 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 * */ /* * admin/edit/kdb5_edit.c * * (C) Copyright 1990,1991, 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. * * * Edit a KDC database. */ /* * Copyright (C) 1998 by the FundsXpress, INC. * * 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 FundsXpress. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. FundsXpress makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ /* * Yes, I know this is a hack, but we need admin.h without including the * rpc.h header. Additionally, our rpc.h header brings in * a des.h header which causes other problems. */ #define _RPC_RPC_H #include #include #include #include #include #include #include #include #include #include "kdb5_util.h" char *Err_no_master_msg = "Master key not entered!\n"; char *Err_no_database = "Database not currently opened!\n"; /* * XXX Ick, ick, ick. These global variables shouldn't be global.... */ char *mkey_password = 0; /* * I can't figure out any way for this not to be global, given how ss * works. */ int exit_status = 0; krb5_context util_context; kadm5_config_params global_params; void usage() { fprintf(stderr, "%s: " "kdb5_util [-x db_args]* [-r realm] [-d dbname] [-k mkeytype] [-M mkeyname]\n" "\t [-sf stashfilename] [-P password] [-m] cmd [cmd_options]\n" "\tcreate [-s]\n" "\tdestroy [-f]\n" "\tstash [-f keyfile]\n" "\tdump [-old] [-ov] [-b6] [-verbose] [filename [princs...]]\n" "\t [-mkey_convert] [-new_mkey_file mkey_file]\n" "\t [-rev] [-recurse] [filename [princs...]]\n" "\tload [-old] [-ov] [-b6] [-verbose] [-update] filename\n" "\tark [-e etype_list] principal\n" "\nwhere,\n\t[-x db_args]* - any number of database specific arguments.\n" "\t\t\tLook at each database documentation for supported arguments\n", gettext("Usage")); exit(1); } krb5_keyblock master_key; extern krb5_principal master_princ; krb5_db_entry master_entry; int valid_master_key = 0; char *progname; krb5_boolean manual_mkey = FALSE; krb5_boolean dbactive = FALSE; static int open_db_and_mkey(void); static void add_random_key(int, char **); typedef void (*cmd_func)(int, char **); struct _cmd_table { char *name; cmd_func func; int opendb; } cmd_table[] = { {"create", kdb5_create, 0}, {"destroy", kdb5_destroy, 1}, {"stash", kdb5_stash, 1}, {"dump", dump_db, 1}, {"load", load_db, 0}, {"ark", add_random_key, 1}, {NULL, NULL, 0}, }; static struct _cmd_table *cmd_lookup(name) char *name; { struct _cmd_table *cmd = cmd_table; while (cmd->name) { if (strcmp(cmd->name, name) == 0) return cmd; else cmd++; } return NULL; } #define ARG_VAL (--argc > 0 ? (koptarg = *(++argv)) : (char *)(usage(), NULL)) char **db5util_db_args = NULL; int db5util_db_args_size = 0; static void extended_com_err_fn (const char *myprog, errcode_t code, const char *fmt, va_list args) { const char *emsg; if (code) { emsg = krb5_get_error_message (util_context, code); fprintf (stderr, "%s: %s ", myprog, emsg); krb5_free_error_message (util_context, emsg); } else { fprintf (stderr, "%s: ", myprog); } vfprintf (stderr, fmt, args); fprintf (stderr, "\n"); } int add_db_arg(char *arg) { char **temp; db5util_db_args_size++; temp = realloc(db5util_db_args, sizeof(char *) * (db5util_db_args_size + 1)); if (temp == NULL) return 0; db5util_db_args = temp; db5util_db_args[db5util_db_args_size-1] = arg; db5util_db_args[db5util_db_args_size] = NULL; return 1; } int main(argc, argv) int argc; char *argv[]; { struct _cmd_table *cmd = NULL; char *koptarg, **cmd_argv; char *db_name_tmp = NULL; int cmd_argc; krb5_error_code retval; (void) setlocale(LC_ALL, ""); set_com_err_hook(extended_com_err_fn); #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); Err_no_master_msg = gettext("Master key not entered!\n"); Err_no_database = gettext("Database not currently opened!\n"); /* * Solaris Kerberos: * Ensure that "progname" is set before calling com_err. */ progname = (strrchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0]); retval = kadm5_init_krb5_context(&util_context); if (retval) { com_err (progname, retval, gettext("while initializing Kerberos code")); exit(1); } cmd_argv = (char **) malloc(sizeof(char *)*argc); if (cmd_argv == NULL) { com_err(progname, ENOMEM, gettext("while creating sub-command arguments")); exit(1); } memset(cmd_argv, 0, sizeof(char *)*argc); cmd_argc = 1; argv++; argc--; while (*argv) { if (strcmp(*argv, "-P") == 0 && ARG_VAL) { mkey_password = koptarg; manual_mkey = TRUE; } else if (strcmp(*argv, "-d") == 0 && ARG_VAL) { global_params.dbname = koptarg; global_params.mask |= KADM5_CONFIG_DBNAME; db_name_tmp = malloc( strlen(global_params.dbname) + sizeof("dbname=")); if( db_name_tmp == NULL ) { com_err(progname, ENOMEM, "while parsing command arguments"); exit(1); } strcpy( db_name_tmp, "dbname="); strcat( db_name_tmp, global_params.dbname ); if (!add_db_arg(db_name_tmp)) { com_err(progname, ENOMEM, "while parsing command arguments\n"); exit(1); } } else if (strcmp(*argv, "-x") == 0 && ARG_VAL) { if (!add_db_arg(koptarg)) { com_err(progname, ENOMEM, "while parsing command arguments\n"); exit(1); } } else if (strcmp(*argv, "-r") == 0 && ARG_VAL) { global_params.realm = koptarg; global_params.mask |= KADM5_CONFIG_REALM; /* not sure this is really necessary */ if ((retval = krb5_set_default_realm(util_context, global_params.realm))) { com_err(progname, retval, gettext("while setting default " "realm name")); exit(1); } } else if (strcmp(*argv, "-k") == 0 && ARG_VAL) { if (krb5_string_to_enctype(koptarg, &global_params.enctype)) { /* Solaris Kerberos */ com_err(progname, 0, gettext("%s is an invalid enctype"), koptarg); } else global_params.mask |= KADM5_CONFIG_ENCTYPE; } else if (strcmp(*argv, "-M") == 0 && ARG_VAL) { global_params.mkey_name = koptarg; global_params.mask |= KADM5_CONFIG_MKEY_NAME; } else if (((strcmp(*argv, "-sf") == 0) /* SUNWresync121 - carry the old -f forward too */ || (strcmp(*argv, "-f") == 0)) && ARG_VAL) { global_params.stash_file = koptarg; global_params.mask |= KADM5_CONFIG_STASH_FILE; } else if (strcmp(*argv, "-m") == 0) { manual_mkey = TRUE; global_params.mkey_from_kbd = 1; global_params.mask |= KADM5_CONFIG_MKEY_FROM_KBD; } else if (cmd_lookup(*argv) != NULL) { if (cmd_argv[0] == NULL) cmd_argv[0] = *argv; else usage(); } else { cmd_argv[cmd_argc++] = *argv; } argv++; argc--; } if (cmd_argv[0] == NULL) usage(); if( !util_context->default_realm ) { char *temp = NULL; retval = krb5_get_default_realm(util_context, &temp); if( retval ) { com_err (progname, retval, "while getting default realm"); exit(1); } util_context->default_realm = temp; } retval = kadm5_get_config_params(util_context, 1, &global_params, &global_params); if (retval) { /* Solaris Kerberos */ com_err(progname, retval, gettext("while retreiving configuration parameters")); exit(1); } /* * Dump creates files which should not be world-readable. It is * easiest to do a single umask call here. */ (void) umask(077); (void) memset(&master_key, 0, sizeof (krb5_keyblock)); if ((global_params.enctype != ENCTYPE_UNKNOWN) && (!krb5_c_valid_enctype(global_params.enctype))) { /* Solaris Kerberos */ com_err(progname, KRB5_PROG_KEYTYPE_NOSUPP, gettext("while setting up enctype %d"), global_params.enctype); exit(1); } cmd = cmd_lookup(cmd_argv[0]); if (cmd->opendb && open_db_and_mkey()) return exit_status; if (global_params.iprop_enabled == TRUE) ulog_set_role(util_context, IPROP_MASTER); else ulog_set_role(util_context, IPROP_NULL); (*cmd->func)(cmd_argc, cmd_argv); if( db_name_tmp ) free( db_name_tmp ); if( db5util_db_args ) free(db5util_db_args); kadm5_free_config_params(util_context, &global_params); krb5_free_context(util_context); return exit_status; } #if 0 /* * This function is no longer used in kdb5_util (and it would no * longer work, anyway). */ void set_dbname(argc, argv) int argc; char *argv[]; { krb5_error_code retval; if (argc < 3) { /* Solaris Kerberos */ com_err(progname, 0, gettext("Too few arguments")); com_err(progname, 0, gettext("Usage: %s dbpathname realmname"), progname); exit_status++; return; } if (dbactive) { if ((retval = krb5_db_fini(util_context)) && retval!= KRB5_KDB_DBNOTINITED) { /* Solaris Kerberos */ com_err(progname, retval, gettext("while closing previous database")); exit_status++; return; } if (valid_master_key) { krb5_free_keyblock_contents(util_context, &master_key); master_key.contents = NULL; valid_master_key = 0; } krb5_free_principal(util_context, master_princ); dbactive = FALSE; } /* Solaris Kerberos */ (void) set_dbname_help(progname, argv[1]); return; } #endif /* * open_db_and_mkey: Opens the KDC and policy database, and sets the * global master_* variables. Sets dbactive to TRUE if the databases * are opened, and valid_master_key to 1 if the global master * variables are set properly. Returns 0 on success, and 1 on * failure, but it is not considered a failure if the master key * cannot be fetched (the master key stash file may not exist when the * program is run). */ static int open_db_and_mkey() { krb5_error_code retval; int nentries; krb5_boolean more; krb5_data scratch, pwd, seed; dbactive = FALSE; valid_master_key = 0; if ((retval = krb5_db_open(util_context, db5util_db_args, KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_ADMIN))) { com_err(progname, retval, "while initializing database"); exit_status++; return(1); } /* assemble & parse the master key name */ if ((retval = krb5_db_setup_mkey_name(util_context, global_params.mkey_name, global_params.realm, 0, &master_princ))) { com_err(progname, retval, gettext("while setting up master key name")); exit_status++; return(1); } nentries = 1; if ((retval = krb5_db_get_principal(util_context, master_princ, &master_entry, &nentries, &more))) { com_err(progname, retval, gettext("while retrieving master entry")); exit_status++; (void) krb5_db_fini(util_context); return(1); } else if (more) { com_err(progname, KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE, gettext("while retrieving master entry")); exit_status++; (void) krb5_db_fini(util_context); return(1); } else if (!nentries) { com_err(progname, KRB5_KDB_NOENTRY, gettext("while retrieving master entry")); exit_status++; (void) krb5_db_fini(util_context); return(1); } krb5_db_free_principal(util_context, &master_entry, nentries); /* the databases are now open, and the master principal exists */ dbactive = TRUE; if (mkey_password) { pwd.data = mkey_password; pwd.length = strlen(mkey_password); retval = krb5_principal2salt(util_context, master_princ, &scratch); if (retval) { com_err(progname, retval, gettext("while calculated master key salt")); /* Solaris Kerberos */ exit_status++; return(1); } /* If no encryption type is set, use the default */ if (global_params.enctype == ENCTYPE_UNKNOWN) { global_params.enctype = DEFAULT_KDC_ENCTYPE; if (!krb5_c_valid_enctype(global_params.enctype)) com_err(progname, KRB5_PROG_KEYTYPE_NOSUPP, gettext("while setting up enctype %d"), global_params.enctype); } retval = krb5_c_string_to_key(util_context, global_params.enctype, &pwd, &scratch, &master_key); if (retval) { com_err(progname, retval, gettext("while transforming master key from password")); /* Solaris Kerberos */ exit_status++; return(1); } free(scratch.data); mkey_password = 0; } else if ((retval = krb5_db_fetch_mkey(util_context, master_princ, global_params.enctype, manual_mkey, FALSE, global_params.stash_file, 0, &master_key))) { com_err(progname, retval, gettext("while reading master key")); com_err(progname, 0, gettext("Warning: proceeding without master key")); /* * Solaris Kerberos: We don't want to count as an error if for instance * the stash file is not present and we are trying to automate * propagation, which really doesn't need a master key to do so. */ if (retval != KRB5_KDB_CANTREAD_STORED) exit_status++; return(0); } if ((retval = krb5_db_verify_master_key(util_context, master_princ, &master_key))) { com_err(progname, retval, gettext("while verifying master key")); exit_status++; krb5_free_keyblock_contents(util_context, &master_key); return(1); } seed.length = master_key.length; seed.data = (char *)master_key.contents; if ((retval = krb5_c_random_seed(util_context, &seed))) { com_err(progname, retval, gettext("while initializing random key generator")); exit_status++; krb5_free_keyblock_contents(util_context, &master_key); return(1); } valid_master_key = 1; dbactive = TRUE; return 0; } #ifdef HAVE_GETCWD #undef getwd #endif int quit() { krb5_error_code retval; static krb5_boolean finished = 0; if (finished) return 0; retval = krb5_db_fini(util_context); krb5_free_keyblock_contents(util_context, &master_key); finished = TRUE; krb5_free_context(util_context); if (retval && retval != KRB5_KDB_DBNOTINITED) { com_err(progname, retval, gettext("while closing database")); exit_status++; return 1; } return 0; } static void add_random_key(argc, argv) int argc; char **argv; { krb5_error_code ret; krb5_principal princ; krb5_db_entry dbent; int n; krb5_boolean more; krb5_timestamp now; krb5_key_salt_tuple *keysalts = NULL; krb5_int32 num_keysalts = 0; int free_keysalts; /* Solaris Kerberos */ char *me = progname; char *ks_str = NULL; char *pr_str; if (argc < 2) usage(); for (argv++, argc--; *argv; argv++, argc--) { if (!strcmp(*argv, "-e")) { argv++; argc--; ks_str = *argv; continue; } else break; } if (argc < 1) usage(); pr_str = *argv; ret = krb5_parse_name(util_context, pr_str, &princ); if (ret) { com_err(me, ret, gettext("while parsing principal name %s"), pr_str); exit_status++; return; } n = 1; ret = krb5_db_get_principal(util_context, princ, &dbent, &n, &more); if (ret) { com_err(me, ret, gettext("while fetching principal %s"), pr_str); exit_status++; return; } if (n != 1) { fprintf(stderr, gettext("principal %s not found\n"), pr_str); exit_status++; return; } if (more) { fprintf(stderr, gettext("principal %s not unique\n"), pr_str); krb5_db_free_principal(util_context, &dbent, 1); exit_status++; return; } ret = krb5_string_to_keysalts(ks_str, ", \t", ":.-", 0, &keysalts, &num_keysalts); if (ret) { com_err(me, ret, gettext("while parsing keysalts %s"), ks_str); exit_status++; return; } if (!num_keysalts || keysalts == NULL) { num_keysalts = global_params.num_keysalts; keysalts = global_params.keysalts; free_keysalts = 0; } else free_keysalts = 1; ret = krb5_dbe_ark(util_context, &master_key, keysalts, num_keysalts, &dbent); if (free_keysalts) free(keysalts); if (ret) { com_err(me, ret, gettext("while randomizing principal %s"), pr_str); krb5_db_free_principal(util_context, &dbent, 1); exit_status++; return; } dbent.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE; ret = krb5_timeofday(util_context, &now); if (ret) { com_err(me, ret, gettext("while getting time")); krb5_db_free_principal(util_context, &dbent, 1); exit_status++; return; } ret = krb5_dbe_update_last_pwd_change(util_context, &dbent, now); if (ret) { com_err(me, ret, gettext("while setting changetime")); krb5_db_free_principal(util_context, &dbent, 1); exit_status++; return; } ret = krb5_db_put_principal(util_context, &dbent, &n); krb5_db_free_principal(util_context, &dbent, 1); if (ret) { com_err(me, ret, gettext("while saving principal %s"), pr_str); exit_status++; return; } printf("%s changed\n", pr_str); } /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _KDB5_UTIL_H #define _KDB5_UTIL_H /* * 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 * */ #ifdef __cplusplus extern "C" { #endif /* * admin/edit/kdb5_edit.h * * Copyright 1992 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. * */ #include #define MAX_HEADER 1024 #define REALM_SEP '@' #define REALM_SEP_STR "@" extern char *progname; extern char *Err_no_database; #ifndef V4_DECLARES_STATIC extern krb5_keyblock master_keyblock; extern krb5_principal master_princ; #endif extern krb5_boolean dbactive; extern int exit_status; extern krb5_context util_context; extern kadm5_config_params global_params; extern int valid_master_key; extern krb5_db_entry master_db; extern char **db5util_db_args; extern int db5util_db_args_size; extern int add_db_arg(char *arg); extern void usage(void); extern void add_key (char const *, char const *, krb5_const_principal, const krb5_keyblock *, krb5_kvno, krb5_keysalt *); extern int set_dbname_help (char *, char *); extern char *kdb5_util_Init (int, char **); extern int quit (void); extern int check_for_match (char *, int, krb5_db_entry *, int, int); extern void parse_token (char *, int *, int *, char *); extern int create_db_entry (krb5_principal, krb5_db_entry *); extern int kadm5_create_magic_princs (kadm5_config_params *params, krb5_context context); extern int process_ov_principal (char *fname, krb5_context kcontext, FILE *filep, int verbose, int *linenop); extern void load_db (int argc, char **argv); extern void dump_db (int argc, char **argv); extern void kdb5_create (int argc, char **argv); extern void kdb5_destroy (int argc, char **argv); extern void kdb5_stash (int argc, char **argv); extern void update_ok_file (char *file_name); extern int kadm5_create (kadm5_config_params *params); void usage (void); #ifdef __cplusplus } #endif #endif /* !_KDB5_UTIL_H */ /* Prototype for nstrtok */ char *nstrtok(char *, const char *delim); /* * 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 * */ #include #include #include #include "autoconf.h" #ifdef HAVE_MEMORY_H #include #endif #include #include #include #include #include "import_err.h" #include "kdb5_util.h" #include "nstrtok.h" #define LINESIZE 32768 /* XXX */ #define PLURAL(count) (((count) == 1) ? error_message(IMPORT_SINGLE_RECORD) : error_message(IMPORT_PLURAL_RECORDS)) static int parse_pw_hist_ent(current, hist) char *current; osa_pw_hist_ent *hist; { int tmp, i, j, ret; char *cp; ret = 0; hist->n_key_data = 1; hist->key_data = (krb5_key_data *) malloc(hist->n_key_data * sizeof(krb5_key_data)); if (hist->key_data == NULL) return ENOMEM; memset(hist->key_data, 0, sizeof(krb5_key_data)*hist->n_key_data); for (i = 0; i < hist->n_key_data; i++) { krb5_key_data *key_data = &hist->key_data[i]; key_data->key_data_ver = 1; if((cp = nstrtok((char *) NULL, "\t")) == NULL) { com_err(NULL, IMPORT_BAD_RECORD, "%s", current); ret = IMPORT_FAILED; goto done; } key_data->key_data_type[0] = atoi(cp); if((cp = nstrtok((char *) NULL, "\t")) == NULL) { com_err(NULL, IMPORT_BAD_RECORD, "%s", current); ret = IMPORT_FAILED; goto done; } key_data->key_data_length[0] = atoi(cp); if((cp = nstrtok((char *) NULL, "\t")) == NULL) { com_err(NULL, IMPORT_BAD_RECORD, "%s", current); ret = IMPORT_FAILED; goto done; } if(!(key_data->key_data_contents[0] = (krb5_octet *) malloc(key_data->key_data_length[0]+1))) { ret = ENOMEM; goto done; } for(j = 0; j < key_data->key_data_length[0]; j++) { if(sscanf(cp, "%02x", &tmp) != 1) { com_err(NULL, IMPORT_BAD_RECORD, "%s", current); ret = IMPORT_FAILED; goto done; } key_data->key_data_contents[0][j] = tmp; cp = strchr(cp, ' ') + 1; } } done: return ret; } /* * Function: parse_principal * * Purpose: parse principal line in db dump file * * Arguments: * 0 on success, error code on failure * * Requires: * principal database to be opened. * nstrtok(3) to have a valid buffer in memory. * * Effects: * [effects] * * Modifies: * [modifies] * */ int process_ov_principal(fname, kcontext, filep, verbose, linenop) char *fname; krb5_context kcontext; FILE *filep; int verbose; int *linenop; { XDR xdrs; osa_princ_ent_t rec; krb5_error_code ret; krb5_tl_data tl_data; krb5_principal princ; krb5_db_entry kdb; char *current; char *cp; int x, one; krb5_boolean more; char line[LINESIZE]; if (fgets(line, LINESIZE, filep) == (char *) NULL) { return IMPORT_BAD_FILE; } if((cp = nstrtok(line, "\t")) == NULL) return IMPORT_BAD_FILE; if((rec = (osa_princ_ent_t) malloc(sizeof(osa_princ_ent_rec))) == NULL) return ENOMEM; memset(rec, 0, sizeof(osa_princ_ent_rec)); if((ret = krb5_parse_name(kcontext, cp, &princ))) goto done; krb5_unparse_name(kcontext, princ, ¤t); if((cp = nstrtok((char *) NULL, "\t")) == NULL) { com_err(NULL, IMPORT_BAD_RECORD, "%s", current); ret = IMPORT_FAILED; goto done; } else { if(strcmp(cp, "")) { if((rec->policy = (char *) malloc(strlen(cp)+1)) == NULL) { ret = ENOMEM; goto done; } strcpy(rec->policy, cp); } else rec->policy = NULL; } if((cp = nstrtok((char *) NULL, "\t")) == NULL) { com_err(NULL, IMPORT_BAD_RECORD, "%s", current); ret = IMPORT_FAILED; goto done; } rec->aux_attributes = strtol(cp, (char **)NULL, 16); if((cp = nstrtok((char *) NULL, "\t")) == NULL) { com_err(NULL, IMPORT_BAD_RECORD, "%s", current); ret = IMPORT_FAILED; goto done; } rec->old_key_len = atoi(cp); if((cp = nstrtok((char *) NULL, "\t")) == NULL) { com_err(NULL, IMPORT_BAD_RECORD, "%s", current); ret = IMPORT_FAILED; goto done; } rec->old_key_next = atoi(cp); if((cp = nstrtok((char *) NULL, "\t")) == NULL) { com_err(NULL, IMPORT_BAD_RECORD, "%s", current); ret = IMPORT_FAILED; goto done; } rec->admin_history_kvno = atoi(cp); if (! rec->old_key_len) { rec->old_keys = NULL; } else { if(!(rec->old_keys = (osa_pw_hist_ent *) malloc(sizeof(osa_pw_hist_ent) * rec->old_key_len))) { ret = ENOMEM; goto done; } memset(rec->old_keys,0, sizeof(osa_pw_hist_ent) * rec->old_key_len); for(x = 0; x < rec->old_key_len; x++) parse_pw_hist_ent(current, &rec->old_keys[x]); } xdralloc_create(&xdrs, XDR_ENCODE); if (! xdr_osa_princ_ent_rec(&xdrs, rec)) { xdr_destroy(&xdrs); ret = KADM5_XDR_FAILURE; goto done; } tl_data.tl_data_type = KRB5_TL_KADM_DATA; tl_data.tl_data_length = xdr_getpos(&xdrs); tl_data.tl_data_contents = (krb5_octet *) xdralloc_getdata(&xdrs); one = 1; ret = krb5_db_get_principal(kcontext, princ, &kdb, &one, &more); if (ret) goto done; ret = krb5_dbe_update_tl_data(kcontext, &kdb, &tl_data); if (ret) goto done; ret = krb5_db_put_principal(kcontext, &kdb, &one); if (ret) goto done; xdr_destroy(&xdrs); (*linenop)++; done: free(current); krb5_free_principal(kcontext, princ); osa_free_princ_ent(rec); return ret; } /* * 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 * */ /* * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. * */ /* String table of messages for kadm5_create */ /* * I18n HACK. We define gettext(s) to be s so that we can extract the * strings here to the .po file. At the end of this file we will undef * gettext. */ #define gettext(s) s char *str_PARSE_NAME = gettext("while parsing admin principal name."); char *str_HISTORY_PARSE_NAME = gettext("while parsing admin history principal name."); char *str_ADMIN_PRINC_EXISTS = gettext("Warning! Admin principal already exists."); char *str_CHANGEPW_PRINC_EXISTS = gettext("Warning! Changepw principal already exists."); char *str_HISTORY_PRINC_EXISTS = gettext("Warning! Admin history principal already exists."); char *str_ADMIN_PRINC_WRONG_ATTRS = gettext("Warning! Admin principal has incorrect attributes.\n" "\tDISALLOW_TGT should be set, and max_life should be three hours.\n" "\tThis program will leave them as-is, but beware!."); char *str_CHANGEPW_PRINC_WRONG_ATTRS = gettext("Warning! Changepw principal has incorrect attributes.\n" "\tDISALLOW_TGT and PW_CHANGE_SERVICE should both be set, and " "max_life should be five minutes.\n" "\tThis program will leave them as-is, but beware!."); char *str_HISTORY_PRINC_WRONG_ATTRS = gettext("Warning! Admin history principal has incorrect attributes.\n" "\tDISALLOW_ALL_TIX should be set.\n" "\tThis program will leave it as-is, but beware!."); char *str_CREATED_PRINC_DB = gettext("%s: Admin principal database created " "(or it already existed).\n"); /* whoami */ char *str_CREATED_POLICY_DB = gettext("%s: Admin policy database created " "(or it already existed).\n"); /* whoami */ char *str_RANDOM_KEY = gettext("while calling random key for %s."); /* principal name */ char *str_ENCRYPT_KEY = gettext("while calling encrypt key for %s."); /* principal name */ char *str_PUT_PRINC = gettext("while storing %s in Kerberos database."); /* principal name */ char *str_CREATING_POLICY_DB = gettext("while creating/opening admin policy database."); char *str_CLOSING_POLICY_DB = gettext("while closing admin policy database."); char *str_CREATING_PRINC_DB = gettext("while creating/opening admin principal database."); char *str_CLOSING_PRINC_DB = gettext("while closing admin principal database."); char *str_CREATING_PRINC_ENTRY = gettext("while creating admin principal " "database entry for %s."); /* princ_name */ char *str_A_PRINC = gettext("a principal"); char *str_UNPARSE_PRINC = gettext("while unparsing principal."); char *str_CREATED_PRINC = gettext("%s: Created %s principal.\n"); /* whoami, princ_name */ char *str_INIT_KDB = gettext("while initializing kdb."); char *str_NO_KDB = gettext("while initializing kdb.\nThe Kerberos KDC database " "needs to exist in /krb5.\nIf you haven't run " "kdb5_create you need to do so before running this command."); char *str_INIT_RANDOM_KEY = gettext("while initializing random key generator."); char *str_TOO_MANY_ADMIN_PRINC = gettext("while fetching admin princ. Can only have one admin principal."); char *str_TOO_MANY_CHANGEPW_PRINC = gettext("while fetching changepw princ. " "Can only have one changepw principal."); char *str_TOO_MANY_HIST_PRINC = gettext("while fetching history princ. " "Can only have one history principal."); char *str_WHILE_DESTROYING_ADMIN_SESSION = gettext("while closing session with admin server and destroying tickets."); #undef gettext /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _STRING_TABLE_H #define _STRING_TABLE_H /* * 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 * */ #ifdef __cplusplus extern "C" { #endif /* * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. * * $Header: /afs/athena.mit.edu/astaff/project/krbdev/.cvsroot/src/kadmin/\ * dbutil/string_table.h,v 1.2 1996/07/22 20:25:25 marc Exp $ * */ #ifndef _OVSEC_ADM_STRINGS_ extern char *str_PARSE_NAME; extern char *str_HISTORY_PARSE_NAME; extern char *str_ADMIN_PRINC_EXISTS; extern char *str_CHANGEPW_PRINC_EXISTS; extern char *str_HISTORY_PRINC_EXISTS; extern char *str_ADMIN_PRINC_WRONG_ATTRS; extern char *str_CHANGEPW_PRINC_WRONG_ATTRS; extern char *str_HISTORY_PRINC_WRONG_ATTRS; extern char *str_CREATED_PRINC_DB; extern char *str_CREATED_POLICY_DB; extern char *str_RANDOM_KEY; extern char *str_ENCRYPT_KEY; extern char *str_PUT_PRINC; extern char *str_CREATING_POLICY_DB; extern char *str_CLOSING_POLICY_DB; extern char *str_CREATING_PRINC_DB; extern char *str_CLOSING_PRINC_DB; extern char *str_CREATING_PRINC_ENTRY; extern char *str_A_PRINC; extern char *str_UNPARSE_PRINC; extern char *str_CREATED_PRINC; extern char *str_INIT_KDB; extern char *str_NO_KDB; extern char *str_INIT_RANDOM_KEY; extern char *str_TOO_MANY_ADMIN_PRINC; extern char *str_TOO_MANY_CHANGEPW_PRINC; extern char *str_TOO_MANY_HIST_PRINC; extern char *str_WHILE_DESTROYING_ADMIN_SESSION; #endif /* _OVSEC_ADM_STRINGS_ */ #ifdef __cplusplus } #endif #endif /* !_STRING_TABLE_H */ /* * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved * */ /* * Copyright (c) 1988 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that: (1) source distributions retain this entire copyright * notice and comment, and (2) distributions including binaries display * the following acknowledgement: ``This product includes software * developed by the University of California, Berkeley and its contributors'' * in the documentation or other materials provided with the distribution * and in all advertising materials mentioning features or use of this * software. Neither the name of the University 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 ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #include #include #include "nstrtok.h" /* * Function: nstrtok * * Purpose: the same as strtok ... just different. does not deal with * multiple tokens in row. * * Arguments: * s (input) string to scan * delim (input) list of delimiters * string or null on error. * * Requires: * nuttin * * Effects: * sets last to string * * Modifies: * last * */ char * nstrtok(s, delim) register char *s; register const char *delim; { register const char *spanp; register int c, sc; char *tok; static char *last; if (s == NULL && (s = last) == NULL) return (NULL); /* * Skip (span) leading delimiters (s += strspn(s, delim), sort of). */ #ifdef OLD cont: c = *s++; for (spanp = delim; (sc = *spanp++) != 0;) { if (c == sc) goto cont; } if (c == 0) { /* no non-delimiter characters */ last = NULL; return (NULL); } tok = s - 1; #else tok = s; #endif /* * Scan token (scan for delimiters: s += strcspn(s, delim), sort of). * Note that delim must have one NUL; we stop if we see that, too. */ for (;;) { c = *s++; spanp = delim; do { if ((sc = *spanp++) == c) { if (c == 0) s = NULL; else s[-1] = 0; last = s; return (tok); } } while (sc != 0); } /* NOTREACHED */ } /* * 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 * */ /* * admin/edit/util.c * * Copyright 1992 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. * * Utilities for kdb5_edit. * * Some routines derived from code contributed by the Sandia National * Laboratories. Sandia National Laboratories also makes no * representations about the suitability of the modifications, or * additions to this software for any purpose. It is provided "as is" * without express or implied warranty. * */ #include #include "./kdb5_edit.h" #ifndef HAVE_STRSTR char * strstr(s1, s2) char *s1; char *s2; { int s2len; int i; char *temp_ptr; temp_ptr = s1; for ( i = 0; i < strlen(s1); i++) { if (memcmp(temp_ptr, s2, strlen(s2)) == 0) return(temp_ptr); temp_ptr += 1; } return ((char *) 0); } #endif /* HAVE_STRSTR */ void parse_token(token_in, must_be_first_char, num_tokens, tokens_out) char *token_in; int *must_be_first_char; int *num_tokens; char *tokens_out; { int i, j; int token_count = 0; i = 0; j = 0; /* Eliminate Up Front Asterisks */ *must_be_first_char = 1; for (i = 0; token_in[i] == '*'; i++) { *must_be_first_char = 0; } if (i == strlen(token_in)) { *num_tokens = 0; return; } /* Fill first token_out */ token_count++; while ((token_in[i] != '*') && (token_in[i] != '\0')) { tokens_out[j] = token_in[i]; j++; i++; } if (i == strlen(token_in)) { tokens_out[j] = '\0'; *num_tokens = token_count; return; } /* Then All Subsequent Tokens */ while (i < strlen(token_in)) { if (token_in[i] == '*') { token_count++; tokens_out[j] = '\t'; } else { tokens_out[j] = token_in[i]; } i++; j++; } tokens_out[j] = '\0'; if (tokens_out[j - 1] == '\t') { token_count--; tokens_out[j - 1] = '\0'; } *num_tokens = token_count; return; } int check_for_match(search_field, must_be_first_character, chk_entry, num_tokens, type) int must_be_first_character; char *search_field; krb5_db_entry *chk_entry; int num_tokens; int type; { char token1[256]; char *found1; char token2[256]; char *found2; char token3[256]; char *found3; char *local_entry; local_entry = chk_entry->princ->data[type].data; token1[0] = token2[0] = token3[0] = '\0'; (void) sscanf(search_field, "%s\t%s\t%s", token1, token2, token3); found1 = strstr(local_entry, token1); if (must_be_first_character && (found1 != local_entry)) return(0); if (found1 && (num_tokens == 1)) return(1); if (found1 && (num_tokens > 1)) { found2 = strstr(local_entry, token2); if (found2 && (found2 > found1) && (num_tokens == 2)) return(1); } if ((found2 > found1) && (num_tokens == 3)) { found3 = strstr(local_entry, token3); if (found3 && (found3 > found2) && (found2 > found1)) return(1); } return(0); } # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. # # Makefile for Kerberos client-install utility. # # Copyright (c) 2018, Joyent, Inc. PROG= ksetpw \ kdyndns \ ksmb \ kconf SHFILES= kclient SECFILES= pam_krb5_first \ pam_krb5_only \ pam_krb5_optional CLOBBERFILES += $(SHFILES) KRB5SBINSHFILES=$(SHFILES:%=$(KRB5SBIN)/%) USRLIBSEC= $(ROOT)/usr/lib/security SEC= $(SECFILES:%=$(USRLIBSEC)/%) $(SEC): FILEMODE = $(LIBFILEMODE) KS_OBJS= ksetpw.o KD_OBJS= kdyndns.o KSMB_OBJS= ksmb.o KC_OBJS= kconf.o OBJS= $(KS_OBJS) $(KD_OBJS) $(KSMB_OBJS) $(KC_OBJS) SSRCS= kclient.sh SRCS= $(OBJS:%.o=%.c) include ../../../Makefile.cmd include $(SRC)/lib/gss_mechs/mech_krb5/Makefile.mech_krb5 POFILE= $(SSRCS:%.sh=%.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 CERRWARN += -Wno-parentheses CERRWARN += -Wno-unused-function # not linted SMATCH=off ksetpw: LDFLAGS += $(KRUNPATH) kdyndns: LDFLAGS += -R/usr/lib/smbsrv ksmb: LDFLAGS += -R/usr/lib/smbsrv # Hammerhead: GNU ld needs rpath-link for transitive deps (libsmb, mech_krb5) via libsmbns kdyndns ksmb: LDFLAGS += -Wl,-rpath-link,$(ROOT)/usr/lib/smbsrv -Wl,-rpath-link,$(ROOT)/usr/lib/gss kconf: LDFLAGS += $(KRUNPATH) KS_LDLIBS = $(LDLIBS) $(KMECHLIB) KD_LDLIBS = $(LDLIBS) -L$(ROOT)/usr/lib/smbsrv -lsmbns KSMB_LDLIBS = $(LDLIBS) -L$(ROOT)/usr/lib/smbsrv -lsmb KC_LDLIBS = $(LDLIBS) $(KMECHLIB) .KEEP_STATE: all: $(PROG) $(SHFILES) $(SEC) install: all $(KRB5SBIN) $(KRB5SBINSHFILES) $(KRB5LIBSHFILES) $(KRB5LIBPROG) kdyndns: $(KD_OBJS) $(LINK.c) $(KD_OBJS) -o $@ $(KD_LDLIBS) $(POST_PROCESS) ksmb: $(KSMB_OBJS) $(LINK.c) $(KSMB_OBJS) -o $@ $(KSMB_LDLIBS) $(POST_PROCESS) ksetpw: $(KS_OBJS) $(LINK.c) $(KS_OBJS) -o $@ $(KS_LDLIBS) $(POST_PROCESS) kconf: $(KC_OBJS) $(LINK.c) $(KC_OBJS) -o $@ $(KC_LDLIBS) $(POST_PROCESS) $(KRB5SBIN): $(INS.dir) $(USRLIBSEC)/%: % $(INS.file) clean: $(RM) $(OBJS) include ../../../Makefile.targ #!/bin/ksh93 -p # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. # Copyright 2014 Nexenta Systems, Inc. All rights reserved. # Copyright 2016 Toomas Soome # # This script is used to setup the Kerberos client by # supplying information about the Kerberos realm and kdc. # # The kerberos configuration file (/etc/krb5/krb5.conf) would # be generated and local host's keytab file setup. The script # can also optionally setup the system to do kerberized nfs and # bringover a master krb5.conf copy from a specified location. # function cleanup { kdestroy -q > $TMP_FILE 2>&1 rm -r $TMPDIR > /dev/null 2>&1 exit $1 } function exiting { printf "\n$(gettext "Exiting setup, nothing changed").\n\n" cleanup $1 } function error_message { printf -- "---------------------------------------------------\n" >&2 printf "$(gettext "Setup FAILED").\n\n" >&2 cleanup 1 } function check_bin { typeset bin=$1 if [[ ! -x $bin ]]; then printf "$(gettext "Could not access/execute %s").\n" $bin >&2 error_message fi } function cannot_create { typeset filename="$1" typeset stat="$2" if [[ $stat -ne 0 ]]; then printf "\n$(gettext "Can not create/edit %s, exiting").\n" $filename >&2 error_message fi } function update_pam_conf { typeset PAM TPAM service PAM=/etc/pam.conf TPAM=$(mktemp -q -t kclient-pamconf.XXXXXX) if [[ -z $TPAM ]]; then printf "\n$(gettext "Can not create temporary file, exiting").\n" >&2 error_message fi cp $PAM $TPAM >/dev/null 2>&1 printf "$(gettext "Configuring %s").\n\n" $PAM for service in $SVCs; do svc=${service%:*} auth_type=${service#*:} if egrep -s "^$svc[ ][ ]*auth.*pam_krb5*" $TPAM; then printf "$(gettext "The %s service is already configured for pam_krb5, please merge this service in %s").\n\n" $svc $PAM >&2 continue else exec 3>>$TPAM printf "\n$svc\tauth include\t\tpam_krb5_$auth_type\n" 1>&3 fi done cp $TPAM $PAM > /dev/null 2>&1 rm $TPAM > /dev/null 2>&1 } function modify_nfssec_conf { typeset NFSSEC_FILE="/etc/nfssec.conf" if [[ -r $NFSSEC_FILE ]]; then cat $NFSSEC_FILE > $NFSSEC_FILE.sav cannot_create $NFSSEC_FILE.sav $? fi cat $NFSSEC_FILE > $TMP_FILE cannot_create $TMP_FILE $? if grep -s "#krb5" $NFSSEC_FILE > /dev/null 2>&1; then sed "s%^#krb5%krb5%" $TMP_FILE >$NFSSEC_FILE cannot_create $NFSSEC_FILE $? fi } function call_kadmin { typeset svc="$1" typeset bool1 bool2 bool3 bool4 typeset service_princ getprincsubcommand anksubcommand ktaddsubcommand typeset ktremsubcommand for listentry in $fqdnlist; do # Reset conditional vars to 1 bool1=1; bool2=1; bool3=1; bool4=1 service_princ=$(echo "${svc}/${listentry}") getprincsubcommand="getprinc $service_princ" anksubcommand="addprinc -randkey $service_princ" ktaddsubcommand="ktadd $service_princ" ktremsubcommand="ktrem $service_princ all" kadmin -c $KRB5CCNAME -q "$getprincsubcommand" 1>$TMP_FILE 2>&1 egrep -s "$(gettext "get_principal: Principal does not exist")" $TMP_FILE bool1=$? egrep -s "$(gettext "get_principal: Operation requires ``get")" $TMP_FILE bool2=$? if [[ $bool1 -eq 0 || $bool2 -eq 0 ]]; then kadmin -c $KRB5CCNAME -q "$anksubcommand" 1>$TMP_FILE 2>&1 egrep -s "$(gettext "add_principal: Principal or policy already exists while creating \"$service_princ@$realm\".")" $TMP_FILE bool3=$? egrep -s "$(gettext "Principal \"$service_princ@$realm\" created.")" $TMP_FILE bool4=$? if [[ $bool3 -eq 0 || $bool4 -eq 0 ]]; then printf "$(gettext "%s entry ADDED to KDC database").\n" $service_princ else cat $TMP_FILE; printf "\n$(gettext "kadmin: add_principal of %s failed, exiting").\n" $service_princ >&2 error_message fi else printf "$(gettext "%s entry already exists in KDC database").\n" $service_princ >&2 fi klist -k 1>$TMP_FILE 2>&1 egrep -s "$service_princ@$realm" $TMP_FILE if [[ $? -eq 0 ]]; then printf "$(gettext "%s entry already present in keytab").\n" $service_princ >&2 # Don't care is this succeeds or not, just need to replace old # entries as it is assummed that the client is reinitialized kadmin -c $KRB5CCNAME -q "$ktremsubcommand" 1>$TMP_FILE 2>&1 fi kadmin -c $KRB5CCNAME -q "$ktaddsubcommand" 1>$TMP_FILE 2>&1 egrep -s "$(gettext "added to keytab WRFILE:$KRB5_KEYTAB_FILE.")" $TMP_FILE if [[ $? -ne 0 ]]; then cat $TMP_FILE; printf "\n$(gettext "kadmin: ktadd of %s failed, exiting").\n" $service_princ >&2 error_message else printf "$(gettext "%s entry ADDED to keytab").\n" $service_princ fi done } function writeup_krb5_conf { typeset dh printf "\n$(gettext "Setting up %s").\n\n" $KRB5_CONFIG_FILE exec 3>$KRB5_CONFIG if [[ $? -ne 0 ]]; then printf "\n$(gettext "Can not write to %s, exiting").\n" $KRB5_CONFIG >&2 error_message fi printf "[libdefaults]\n" 1>&3 if [[ $no_keytab == yes ]]; then printf "\tverify_ap_req_nofail = false\n" 1>&3 fi if [[ $dns_lookup == yes ]]; then printf "\t$dnsarg = on\n" 1>&3 if [[ $dnsarg == dns_lookup_kdc ]]; then printf "\tdefault_realm = $realm\n" 1>&3 printf "\n[domain_realm]\n" 1>&3 if [[ -n $fkdc_list ]]; then for kdc in $fkdc_list; do printf "\t$kdc = $realm\n" 1>&3 done fi printf "\t$FKDC = $realm\n" 1>&3 printf "\t$client_machine = $realm\n" 1>&3 if [[ -z $short_fqdn ]]; then printf "\t.$domain = $realm\n\n" 1>&3 else printf "\t.$short_fqdn = $realm\n\n" 1>&3 fi if [[ -n $domain_list ]]; then for dh in $domain_list; do printf "\t$dh = $realm\n" 1>&3 done fi else if [[ $dnsarg = dns_lookup_realm ]]; then printf "\tdefault_realm = $realm\n" 1>&3 printf "\n[realms]\n" 1>&3 printf "\t$realm = {\n" 1>&3 if [[ -n $kdc_list ]]; then for kdc in $kdc_list; do printf "\t\tkdc = $kdc\n" 1>&3 done else printf "\t\tkdc = $KDC\n" 1>&3 fi printf "\t\tadmin_server = $KDC\n" 1>&3 if [[ $non_solaris == yes ]]; then printf "\n\t\tkpasswd_protocol = SET_CHANGE\n" 1>&3 fi printf "\t}\n\n" 1>&3 else printf "\tdefault_realm = $realm\n\n" 1>&3 fi fi else printf "\tdefault_realm = $realm\n\n" 1>&3 printf "[realms]\n" 1>&3 printf "\t$realm = {\n" 1>&3 if [[ -n $kdc_list ]]; then for kdc in $kdc_list; do printf "\t\tkdc = $kdc\n" 1>&3 done else printf "\t\tkdc = $KDC\n" 1>&3 fi printf "\t\tadmin_server = $KDC\n" 1>&3 if [[ $non_solaris == yes ]]; then printf "\n\t\tkpasswd_protocol = SET_CHANGE\n" 1>&3 fi printf "\t}\n\n" 1>&3 printf "[domain_realm]\n" 1>&3 if [[ -n $fkdc_list ]]; then for kdc in $fkdc_list; do printf "\t$kdc = $realm\n" 1>&3 done fi printf "\t$FKDC = $realm\n" 1>&3 printf "\t$client_machine = $realm\n" 1>&3 if [[ -z $short_fqdn ]]; then printf "\t.$domain = $realm\n\n" 1>&3 else printf "\t.$short_fqdn = $realm\n\n" 1>&3 fi if [[ -n $domain_list ]]; then for dh in $domain_list; do printf "\t$dh = $realm\n" 1>&3 done fi fi printf "[logging]\n" 1>&3 printf "\tdefault = FILE:/var/krb5/kdc.log\n" 1>&3 printf "\tkdc = FILE:/var/krb5/kdc.log\n" 1>&3 printf "\tkdc_rotate = {\n\t\tperiod = 1d\n\t\tversions = 10\n\t}\n\n" 1>&3 printf "[appdefaults]\n" 1>&3 printf "\tkinit = {\n\t\trenewable = true\n\t\tforwardable = true\n" 1>&3 if [[ $no_keytab == yes ]]; then printf "\t\tno_addresses = true\n" 1>&3 fi printf "\t}\n" 1>&3 } function ask { typeset question=$1 typeset default_answer=$2 if [[ -z $default_answer ]]; then printf "$question :" else printf "$question [$default_answer]: " fi read answer test -z "$answer" && answer="$default_answer" } function yesno { typeset question="$1" answer= yn=`printf "$(gettext "y/n")"` y=`printf "$(gettext "y")"` n=`printf "$(gettext "n")"` yes=`printf "$(gettext "yes")"` no=`printf "$(gettext "no")"` while [[ -z $answer ]]; do ask "$question" $yn case $answer in $y|$yes) answer=yes;; $n|$no) answer=no;; *) answer=;; esac done } function query { yesno "$*" if [[ $answer == no ]]; then printf "\t$(gettext "No action performed").\n" fi } function read_profile { typeset param value typeset file="$1" if [[ ! -d $file && -r $file ]]; then while read param value do case $param in REALM) if [[ -z $realm ]]; then realm="$value" checkval="REALM"; check_value $realm fi ;; KDC) if [[ -z $KDC ]]; then KDC="$value" checkval="KDC"; check_value $KDC fi ;; ADMIN) if [[ -z $ADMIN_PRINC ]]; then ADMIN_PRINC="$value" checkval="ADMIN_PRINC" check_value $ADMIN_PRINC fi ;; FILEPATH) if [[ -z $filepath ]]; then filepath="$value" fi ;; NFS) if [[ -z $add_nfs ]]; then if [[ $value == 1 ]]; then add_nfs=yes else add_nfs=no fi fi ;; NOKEY) if [[ -z $no_keytab ]]; then if [[ $value == 1 ]]; then no_keytab=yes else no_keytab=no fi fi ;; NOSOL) if [[ -z $non_solaris ]]; then if [[ $value == 1 ]]; then non_solaris=yes no_keytab=yes else non_solaris=no fi fi ;; LHN) if [[ -z $logical_hn ]]; then logical_hn="$value" checkval="LOGICAL_HOSTNAME" check_value $logical_hn fi ;; DNSLOOKUP) if [[ -z $dnsarg ]]; then dnsarg="$value" checkval="DNS_OPTIONS" check_value $dnsarg fi ;; FQDN) if [[ -z $fqdnlist ]]; then fqdnlist="$value" checkval="FQDN" check_value $fqdnlist verify_fqdnlist "$fqdnlist" fi ;; MSAD) if [[ -z $msad ]]; then if [[ $value == 1 ]]; then msad=yes non_solaris=yes else msad=no fi fi ;; esac done <$file else printf "\n$(gettext "The kclient profile \`%s' is not valid, exiting").\n" $file >&2 error_message fi } function ping_check { typeset machine="$1" typeset string="$2" if ping $machine 2 > /dev/null 2>&1; then : else printf "\n$(gettext "%s %s is unreachable, exiting").\n" $string $machine >&2 error_message fi # Output timesync warning if not using a profile, i.e. in # interactive mode. if [[ -z $profile && $string == KDC ]]; then # It's difficult to sync up time with KDC esp. if in a # zone so just print a warning about KDC time sync. printf "\n$(gettext "Note, this system and the KDC's time must be within 5 minutes of each other for Kerberos to function").\n" >&2 printf "$(gettext "Both systems should run some form of time synchronization system like Network Time Protocol (NTP)").\n" >&2 break fi } function check_value { typeset arg="$1" if [[ -z $arg ]]; then printf "\n$(gettext "No input obtained for %s, exiting").\n" $checkval >&2 error_message else echo "$arg" > $TMP_FILE if egrep -s '[*$^#!]+' $TMP_FILE; then printf "\n$(gettext "Invalid input obtained for %s, exiting").\n" $checkval >&2 error_message fi fi } function set_dns_value { typeset -l arg="$1" if [[ $arg == dns_lookup_kdc || $arg == dns_lookup_realm || $arg == dns_fallback ]]; then dns_lookup=yes else if [[ $arg == none ]]; then dns_lookup=no else printf "\n$(gettext "Invalid DNS lookup option, exiting").\n" >&2 error_message fi fi } function verify_kdcs { typeset k_list="$1" typeset -l kdc typeset list fqhn f_list kdc_list=$(echo "$k_list" | sed 's/,/ /g') if [[ -z $k_list ]]; then printf "\n$(gettext "At least one KDC should be listed").\n\n" >&2 usage fi for kdc in $k_list; do if [[ $kdc != $KDC ]]; then list="$list $kdc" fkdc=`$KLOOKUP $kdc` if ping $fkdc 2 > /dev/null; then : else printf "\n$(gettext "%s %s is unreachable, no action performed").\n" "KDC" $fkdc >&2 fi f_list="$f_list $fkdc" fi done fkdc_list="$f_list" kdc_list="$list" } function parse_service { typeset service_list=$1 service_list=${service_list//,/ } for service in $service_list; do svc=${service%:} auth_type=${service#:} [[ -z $svc || -z $auth_type ]] && return print -- $svc $auth_type done } function verify_fqdnlist { typeset list="$1" typeset -l hostname typeset -i count=1 typeset fqdnlist eachfqdn tmpvar fullhost list=$(echo "$list" | tr -d " " | tr -d "\t") hostname=$(uname -n | cut -d"." -f1) fqdnlist=$client_machine eachfqdn=$(echo "$list" | cut -d"," -f$count) if [[ -z $eachfqdn ]]; then printf "\n$(gettext "If the -f option is used, at least one FQDN should be listed").\n\n" >&2 usage else while [[ ! -z $eachfqdn ]]; do tmpvar=$(echo "$eachfqdn" | cut -d"." -f1) if [[ -z $tmpvar ]]; then fullhost="$hostname$eachfqdn" else fullhost="$hostname.$eachfqdn" fi ping_check $fullhost $(gettext "System") if [[ $fullhost == $client_machine ]]; then : else fqdnlist="$fqdnlist $fullhost" fi if [[ $list == *,* ]]; then ((count = count + 1)) eachfqdn=$(echo "$list" | cut -d"," -f$count) else break fi done fi } function setup_keytab { typeset cname ask_fqdns current_release # # 1. kinit with ADMIN_PRINC # if [[ -z $ADMIN_PRINC ]]; then printf "\n$(gettext "Enter the krb5 administrative principal to be used"): " read ADMIN_PRINC checkval="ADMIN_PRINC"; check_value $ADMIN_PRINC fi echo "$ADMIN_PRINC">$TMP_FILE [[ -n $msad ]] && return if egrep -s '\/admin' $TMP_FILE; then # Already in "/admin" format, do nothing : else if egrep -s '\/' $TMP_FILE; then printf "\n$(gettext "Improper entry for krb5 admin principal, exiting").\n" >&2 error_message else ADMIN_PRINC=$(echo "$ADMIN_PRINC/admin") fi fi printf "$(gettext "Obtaining TGT for %s") ...\n" $ADMIN_PRINC cname=$(canon_resolve $KDC) if [[ -n $cname ]]; then kinit -S kadmin/$cname $ADMIN_PRINC else kinit -S kadmin/$FKDC $ADMIN_PRINC fi klist 1>$TMP_FILE 2>&1 if egrep -s "$(gettext "Valid starting")" $TMP_FILE && egrep -s "kadmin/$FKDC@$realm" $TMP_FILE; then : else printf "\n$(gettext "kinit of %s failed, exiting").\n" $ADMIN_PRINC >&2 error_message fi # # 2. Do we want to create and/or add service principal(s) for fqdn's # other than the one listed in resolv.conf(5) ? # if [[ -z $options ]]; then query "$(gettext "Do you have multiple DNS domains spanning the Kerberos realm") $realm ?" ask_fqdns=$answer if [[ $ask_fqdns == yes ]]; then printf "$(gettext "Enter a comma-separated list of DNS domain names"): " read fqdnlist verify_fqdnlist "$fqdnlist" else fqdnlist=$client_machine fi else if [[ -z $fqdnlist ]]; then fqdnlist=$client_machine fi fi if [[ $add_nfs == yes ]]; then echo; call_kadmin nfs fi # Add the host entry to the keytab echo; call_kadmin host } function setup_lhn { typeset -l logical_hn echo "$logical_hn" > $TMP_FILE if egrep -s '[^.]\.[^.]+$' $TMP_FILE; then # do nothing, logical_hn is in fqdn format : else if egrep -s '\.+' $TMP_FILE; then printf "\n$(gettext "Improper format of logical hostname, exiting").\n" >&2 error_message else # Attach fqdn to logical_hn, to get the Fully Qualified # Host Name of the client requested logical_hn=$(echo "$logical_hn.$fqdn") fi fi client_machine=$logical_hn ping_check $client_machine $(gettext "System") } function usage { printf "\n$(gettext "Usage: kclient [ options ]")\n" >&2 printf "\t$(gettext "where options are any of the following")\n\n" >&2 printf "\t$(gettext "[ -D domain_list ] configure a client that has mul tiple mappings of doamin and/or hosts to the default realm")\n" >&2 printf "\t$(gettext "[ -K ] configure a client that does not have host/service keys")\n" >&2 printf "\t$(gettext "[ -R realm ] specifies the realm to use")\n" >&2 printf "\t$(gettext "[ -T kdc_vendor ] specifies which KDC vendor is the server")\n" >&2 printf "\t$(gettext "[ -a adminuser ] specifies the Kerberos administrator")\n" >&2 printf "\t$(gettext "[ -c filepath ] specifies the krb5.conf path used to configure this client")\n" >&2 printf "\t$(gettext "[ -d dnsarg ] specifies which information should be looked up in DNS (dns_lookup_kdc, dns_lookup_realm, and dns_fallback)")\n" >&2 printf "\t$(gettext "[ -f fqdn_list ] specifies which domains to configure host keys for this client")\n" >&2 printf "\t$(gettext "[ -h logicalhostname ] configure the logical host name for a client that is in a cluster")\n" >&2 printf "\t$(gettext "[ -k kdc_list ] specify multiple KDCs, if -m is not used the first KDC in the list is assumed to be the master. KDC host names are used verbatim.")\n" >&2 printf "\t$(gettext "[ -m master ] master KDC server host name")\n" >&2 printf "\t$(gettext "[ -n ] configure client to be an NFS client")\n" >&2 printf "\t$(gettext "[ -p profile ] specifies which profile file to use to configure this client")\n" >&2 printf "\t$(gettext "[ -s pam_list ] update the service for Kerberos authentication")\n" >&2 error_message } function discover_domain { typeset dom DOMs if [[ -z $realm ]]; then set -A DOMs -- `$KLOOKUP _ldap._tcp.dc._msdcs S` else set -A DOMs -- `$KLOOKUP _ldap._tcp.dc._msdcs.$realm S` fi [[ -z ${DOMs[0]} ]] && return 1 dom=${DOMs[0]} dom=${dom#*.} dom=${dom% *} domain=$dom return 0 } function check_nss_hosts_or_ipnodes_config { typeset backend for backend in $1 do [[ $backend == dns ]] && return 0 done return 1 } function check_nss_conf { typeset i j hosts_config for i in hosts ipnodes do grep "^${i}:" /etc/nsswitch.conf|read j hosts_config check_nss_hosts_or_ipnodes_config "$hosts_config" || return 1 done return 0 } function canon_resolve { typeset name ip name=`$KLOOKUP $1 C` [[ -z $name ]] && name=`$KLOOKUP $1 A` [[ -z $name ]] && return ip=`$KLOOKUP $name I` [[ -z $ip ]] && return for i in $ip do if ping $i 2 > /dev/null 2>&1; then break else i= fi done cname=`$KLOOKUP $ip P` [[ -z $cname ]] && return print -- "$cname" } function rev_resolve { typeset name ip ip=`$KLOOKUP $1 I` [[ -z $ip ]] && return name=`$KLOOKUP $ip P` [[ -z $name ]] && return print -- $name } # Convert an AD-style domain DN to a DNS domainname function dn2dns { typeset OIFS dname dn comp components dn=$1 dname= OIFS="$IFS" IFS=, set -A components -- $1 IFS="$OIFS" for comp in "${components[@]}" do [[ "$comp" == [dD][cC]=* ]] || continue dname="$dname.${comp#??=}" done print ${dname#.} } # Form a base DN from a DNS domainname and container function getBaseDN { if [[ -n "$2" ]] then baseDN="CN=$1,$(dns2dn $2)" else baseDN="$(dns2dn $2)" fi } # Convert a DNS domainname to an AD-style DN for that domain function dns2dn { typeset OIFS dn labels OIFS="$IFS" IFS=. set -A labels -- $1 IFS="$OIFS" dn= for label in "${labels[@]}" do dn="${dn},DC=$label" done print -- "${dn#,}" } function getSRVs { typeset srv port $KLOOKUP $1 S | while read srv port do if ping $srv 2 > /dev/null 2>&1; then print -- $srv $port fi done } function getKDC { typeset j set -A KPWs -- $(getSRVs _kpasswd._tcp.$dom.) kpasswd=${KPWs[0]} if [[ -n $siteName ]] then set -A KDCs -- $(getSRVs _kerberos._tcp.$siteName._sites.$dom.) kdc=${KDCs[0]} [[ -n $kdc ]] && return fi # No site name set -A KDCs -- $(getSRVs _kerberos._tcp.$dom.) kdc=${KDCs[0]} [[ -n $kdc ]] && return # Default set -A KDCs -- $DomainDnsZones 88 kdc=$ForestDnsZones } function getDC { typeset j if [[ -n $siteName ]] then set -A DCs -- $(getSRVs _ldap._tcp.$siteName._sites.dc._msdcs.$dom.) dc=${DCs[0]} [[ -n $dc ]] && return fi # No site name set -A DCs -- $(getSRVs _ldap._tcp.dc._msdcs.$dom.) dc=${DCs[0]} [[ -n $dc ]] && return # Default set -A DCs -- $DomainDnsZones 389 dc=$DomainDnsZones } function write_ads_krb5conf { typeset kdcs printf "\n$(gettext "Setting up %s").\n\n" $KRB5_CONFIG_FILE for i in ${KDCs[@]} do [[ $i == +([0-9]) ]] && continue if [[ -n $kdcs ]] then kdcs="$kdcs,$i" else kdcs=$i fi done $KCONF -f $KRB5_CONFIG -r $realm -k $kdcs -m $KDC -p SET_CHANGE -d .$dom if [[ $? -ne 0 ]]; then printf "\n$(gettext "Can not update %s, exiting").\n" $KRB5_CONFIG >&2 error_message fi } function getForestName { ldapsearch -R -T -h $dc $ldap_args \ -b "" -s base "" schemaNamingContext| \ grep ^schemaNamingContext|read j schemaNamingContext if [[ $? -ne 0 ]]; then printf "$(gettext "Can't find forest").\n" >&2 error_message fi schemaNamingContext=${schemaNamingContext#CN=Schema,CN=Configuration,} [[ -z $schemaNamingContext ]] && return 1 forest= while [[ -n $schemaNamingContext ]] do schemaNamingContext=${schemaNamingContext#DC=} forest=${forest}.${schemaNamingContext%%,*} [[ "$schemaNamingContext" = *,* ]] || break schemaNamingContext=${schemaNamingContext#*,} done forest=${forest#.} } function getGC { typeset j [[ -n $gc ]] && return 0 if [[ -n $siteName ]] then set -A GCs -- $(getSRVs _ldap._tcp.$siteName._sites.gc._msdcs.$forest.) gc=${GCs[0]} [[ -n $gc ]] && return fi # No site name set -A GCs -- $(getSRVs _ldap._tcp.gc._msdcs.$forest.) gc=${GCs[0]} [[ -n $gc ]] && return # Default set -A GCs -- $ForestDnsZones 3268 gc=$ForestDnsZones } # # The local variables used to calculate the IP address are of type unsigned # integer (-ui), as this is required to restrict the integer to 32b. # Starting in ksh88, Solaris has incorrectly assummed that -i represents 64b. # function ipAddr2num { typeset OIFS typeset -ui16 num if [[ "$1" != +([0-9]).+([0-9]).+([0-9]).+([0-9]) ]] then print 0 return 0 fi OIFS="$IFS" IFS=. set -- $1 IFS="$OIFS" num=$((${1}<<24 | ${2}<<16 | ${3}<<8 | ${4})) print -- $num } # # The local variables used to calculate the IP address are of type unsigned # integer (-ui), as this is required to restrict the integer to 32b. # Starting in ksh88, Solaris has incorrectly assummed that -i represents 64b. # function num2ipAddr { typeset -ui16 num typeset -ui10 a b c d num=$1 a=$((num>>24 )) b=$((num>>16 & 16#ff)) c=$((num>>8 & 16#ff)) d=$((num & 16#ff)) print -- $a.$b.$c.$d } # # The local variables used to calculate the IP address are of type unsigned # integer (-ui), as this is required to restrict the integer to 32b. # Starting in ksh88, Solaris has incorrectly assummed that -i represents 64b. # function netmask2length { typeset -ui16 netmask typeset -i len netmask=$1 len=32 while [[ $((netmask % 2)) -eq 0 ]] do netmask=$((netmask>>1)) len=$((len - 1)) done print $len } # # The local variables used to calculate the IP address are of type unsigned # integer (-ui), as this is required to restrict the integer to 32b. # Starting in ksh88, Solaris has incorrectly assummed that -i represents 64b. # function getSubnets { typeset -ui16 addr netmask typeset -ui16 classa=16\#ff000000 typeset -ui16 classb=16\#ffff0000 typeset -ui16 classc=16\#ffffff00 ifconfig -a|while read line do addr=0 netmask=0 set -- $line [[ $1 == inet ]] || continue while [[ $# -gt 0 ]] do case "$1" in inet) addr=$(ipAddr2num $2); shift;; netmask) eval netmask=16\#$2; shift;; *) :; esac shift done [[ $addr -eq 0 || $netmask -eq 0 ]] && continue [[ $((addr & classa)) -eq 16\#7f000000 ]] && continue print $(num2ipAddr $((addr & netmask)))/$(netmask2length $netmask) if [ $netmask -gt $classc ] then print $(num2ipAddr $((addr & classc)))/$(netmask2length $classc) fi if [ $netmask -gt $classb ] then print $(num2ipAddr $((addr & classb)))/$(netmask2length $classb) fi if [ $netmask -gt $classa ] then print $(num2ipAddr $((addr & classa)))/$(netmask2length $classa) fi done } function getSite { typeset subnet siteDN j ldapsrv subnet_dom eval "[[ -n \"\$siteName\" ]]" && return for subnet in $(getSubnets) do ldapsearch -R -T -h $dc $ldap_args \ -p 3268 -b "" -s sub cn=$subnet dn |grep ^dn|read j subnetDN [[ -z $subnetDN ]] && continue subnet_dom=$(dn2dns $subnetDN) ldapsrv=$(canon_resolve DomainDnsZones.$subnet_dom) [[ -z $ldapsrv ]] && continue ldapsearch -R -T -h $ldapsrv $ldap_args \ -b "$subnetDN" -s base "" siteObject \ |grep ^siteObject|read j siteDN [[ -z $siteDN ]] && continue eval siteName=${siteDN%%,*} eval siteName=\${siteName#CN=} return done } function doKRB5config { [[ -f $KRB5_CONFIG_FILE ]] && \ cp $KRB5_CONFIG_FILE ${KRB5_CONFIG_FILE}-pre-kclient [[ -f $KRB5_KEYTAB_FILE ]] && \ cp $KRB5_KEYTAB_FILE ${KRB5_KEYTAB_FILE}-pre-kclient [[ -s $KRB5_CONFIG ]] && cp $KRB5_CONFIG $KRB5_CONFIG_FILE [[ -s $KRB5_CONFIG_FILE ]] && chmod 0644 $KRB5_CONFIG_FILE [[ -s $new_keytab ]] && cp $new_keytab $KRB5_KEYTAB_FILE [[ -s $KRB5_KEYTAB_FILE ]] && chmod 0600 $KRB5_KEYTAB_FILE } function addDNSRR { smbFMRI=svc:/network/smb/server:default ddnsProp=smbd/ddns_enable enProp=general/enabled enabled=`svcprop -p $enProp $smbFMRI` ddns_enable=`svcprop -p $ddnsProp $smbFMRI` if [[ $enabled == true && $ddns_enable != true ]]; then printf "$(gettext "Warning: won't create DNS records for client").\n" printf "$(gettext "%s property not set to 'true' for the %s FMRI").\n" $ddnsProp $smbFMRI return fi # Destroy any existing ccache as GSS_C_NO_CREDENTIAL will pick up any # residual default credential in the cache. kdestroy > /dev/null 2>&1 $KDYNDNS -d $1 > /dev/null 2>&1 if [[ $? -ne 0 ]]; then # # Non-fatal, we should carry-on as clients may resolve to # different servers and the client could already exist there. # printf "$(gettext "Warning: unable to create DNS records for client").\n" printf "$(gettext "This could mean that '%s' is not included as a 'nameserver' in the /etc/resolv.conf file or some other type of error").\n" $dc fi } function setSMB { typeset domain=$1 typeset server=$2 smbFMRI=svc:/network/smb/server printf "%s" "$newpw" | $KSMB -d $domain -s $server if [[ $? -ne 0 ]]; then printf "$(gettext "Warning: unable to set %s domain, server and password information").\n" $smbFMRI return fi svcadm restart $smbFMRI > /dev/null 2>&1 if [[ $? -ne 0 ]]; then printf "$(gettext "Warning: unable to restart %s").\n" $smbFMRI fi } function compareDomains { typeset oldDom hspn newDom=$1 # If the client has been previously configured in a different # realm/domain then we need to prompt the user to see if they wish to # switch domains. klist -k 2>&1 | grep @ | read j hspn [[ -z $hspn ]] && return oldDom=${hspn#*@} if [[ $oldDom != $newDom ]]; then printf "$(gettext "The client is currently configured in a different domain").\n" printf "$(gettext "Currently in the '%s' domain, trying to join the '%s' domain").\n" $oldDom $newDom query "$(gettext "Do you want the client to join a new domain") ?" printf "\n" if [[ $answer != yes ]]; then printf "$(gettext "Client will not be joined to the new domain").\n" >&2 error_message fi fi } function getKDCDC { getKDC if [[ -n $kdc ]]; then KDC=$kdc dc=$kdc else getDC if [[ -n $dc ]]; then KDC=$dc else printf "$(gettext "Could not find domain controller server for '%s'. Exiting").\n" $realm >&2 error_message fi fi } function gen_rand { typeset -u hex dd if=/dev/random bs=1 count=1 2>/dev/null | od -A n -tx1 | read hex printf %s $((16#$hex)) } function join_domain { typeset -u upcase_nodename typeset -l locase_nodename typeset -L15 string15 typeset netbios_nodename fqdn container=Computers ldap_args="-o authzid= -o mech=gssapi" userAccountControlBASE=4096 if [[ -z $ADMIN_PRINC ]]; then cprinc=Administrator else cprinc=$ADMIN_PRINC fi if ! discover_domain; then printf "$(gettext "Can not find realm") '%s'.\n" $realm >&2 error_message fi dom=$domain realm=$domain if [[ ${#hostname} -gt 15 ]]; then string15=$hostname upcase_nodename=$string15 locase_nodename=$string15 else upcase_nodename=$hostname locase_nodename=$hostname fi netbios_nodename="${upcase_nodename}\$" fqdn=$hostname.$domain upn=host/${fqdn}@${realm} object=$(mktemp -q -t kclient-computer-object.XXXXXX) if [[ -z $object ]]; then printf "\n$(gettext "Can not create temporary file, exiting").\n " >&2 error_message fi modify_existing=false recreate=false DomainDnsZones=$(rev_resolve DomainDnsZones.$dom.) ForestDnsZones=$(rev_resolve ForestDnsZones.$dom.) getBaseDN "$container" "$dom" if [[ -n $KDC ]]; then dc=$KDC else getKDCDC fi write_ads_krb5conf printf "$(gettext "Attempting to join '%s' to the '%s' domain").\n\n" $upcase_nodename $realm kinit $cprinc@$realm if [[ $? -ne 0 ]]; then printf "$(gettext "Could not authenticate %s. Exiting").\n" $cprinc@$realm >&2 error_message fi if getForestName then printf "\n$(gettext "Forest name found: %s")\n\n" $forest else printf "\n$(gettext "Forest name not found, assuming forest is the domain name").\n" fi getGC getSite if [[ -z $siteName ]] then printf "$(gettext "Site name not found. Local DCs/GCs will not be discovered").\n\n" else printf "$(gettext "Looking for _local_ KDCs, DCs and global catalog servers (SRV RRs)").\n" getKDCDC getGC write_ads_krb5conf fi if [[ ${#GCs} -eq 0 ]]; then printf "$(gettext "Could not find global catalogs. Exiting").\n" >&2 error_message fi # Check to see if the client is transitioning between domains. compareDomains $realm # Here we check domainFunctionality to see which release: # 0, 1, 2: Windows 2000, 2003 Interim, 2003 respecitively # 3: Windows 2008 level=0 ldapsearch -R -T -h "$dc" $ldap_args -b "" -s base "" \ domainControllerFunctionality| grep ^domainControllerFunctionality| \ read j level if [[ $? -ne 0 ]]; then printf "$(gettext "Search for domain functionality failed, exiting").\n" >&2 error_message fi if ldapsearch -R -T -h "$dc" $ldap_args -b "$baseDN" \ -s sub sAMAccountName="$netbios_nodename" dn > /dev/null 2>&1 then : else printf "$(gettext "Search for node failed, exiting").\n" >&2 error_message fi ldapsearch -R -T -h "$dc" $ldap_args -b "$baseDN" -s sub \ sAMAccountName="$netbios_nodename" dn|grep "^dn:"|read j dn if [[ -z $dn ]]; then : # modify_existing is already false, which is what we want. else printf "$(gettext "Computer account '%s' already exists in the '%s' domain").\n" $upcase_nodename $realm query "$(gettext "Do you wish to recreate this computer account") ?" printf "\n" if [[ $answer == yes ]]; then recreate=true else modify_existing=true fi fi if [[ $modify_existing == false && -n $dn ]]; then query "$(gettext "Would you like to delete any sub-object found for this computer account") ?" if [[ $answer == yes ]]; then printf "$(gettext "Looking to see if the machine account contains other objects")...\n" ldapsearch -R -T -h "$dc" $ldap_args -b "$dn" -s sub "" dn | while read j sub_dn do [[ $j != dn: || -z $sub_dn || $dn == $sub_dn ]] && continue if $recreate; then printf "$(gettext "Deleting the following object: %s")\n" ${sub_dn#$dn} ldapdelete -h "$dc" $ldap_args "$sub_dn" > /dev/null 2>&1 if [[ $? -ne 0 ]]; then printf "$(gettext "Error in deleting object: %s").\n" ${sub_dn#$dn} fi else printf "$(gettext "The following object will not be deleted"): %s\n" ${sub_dn#$dn} fi done fi if $recreate; then ldapdelete -h "$dc" $ldap_args "$dn" > /dev/null 2>&1 if [[ $? -ne 0 ]]; then printf "$(gettext "Error in deleting object: %s").\n" ${sub_dn#$dn} >&2 error_message fi elif $modify_existing; then : # Nothing to delete else printf "$(gettext "A machine account already exists").\n" >&2 error_message fi fi [[ -z $dn ]] && dn="CN=${upcase_nodename},${baseDN}" if $modify_existing; then cat > "$object" < /dev/null 2>&1 if [[ $? -ne 0 ]]; then printf "$(gettext "Failed to modify the AD object via LDAP").\n" >&2 error_message fi else dn="CN=${upcase_nodename},${baseDN}" cat > "$object" < /dev/null 2>&1 if [[ $? -ne 0 ]]; then printf "$(gettext "Failed to create the AD object via LDAP").\n" >&2 error_message fi fi # Generate a new password for the new account MAX_PASS=120 i=0 # first check to see if /dev/random exists to generate a new password if [[ ! -e /dev/random ]]; then printf "$(gettext "/dev/random does not exist").\n" >&2 error_message fi while ((MAX_PASS > i)) do # [MS-DISO] A machine password is an ASCII string of randomly # chosen characters. Each character's ASCII code is between 32 # and 122 inclusive. c=$(printf "\\$(printf %o $(($(gen_rand) % 91 + 32)))\n") p="$p$c" ((i+=1)) done newpw=$p if [[ ${#newpw} -ne MAX_PASS ]]; then printf "$(gettext "Password created was of incorrect length").\n" >&2 error_message fi # Set the new password printf "%s" "$newpw" | $KSETPW ${netbios_nodename}@${realm} > /dev/null 2>&1 if [[ $? -ne 0 ]] then printf "$(gettext "Failed to set account password").\n" >&2 error_message fi # Lookup the new principal's kvno: ldapsearch -R -T -h "$dc" $ldap_args -b "$baseDN" \ -s sub cn=$upcase_nodename msDS-KeyVersionNumber| \ grep "^msDS-KeyVersionNumber"|read j kvno [[ -z $kvno ]] && kvno=1 # Set supported enctypes. This only works for Longhorn/Vista, so we # ignore errors here. userAccountControl=$((userAccountControlBASE + 524288 + 65536)) set -A enctypes -- # Do we have local support for AES? encrypt -l|grep ^aes|read j minkeysize maxkeysize val= if [[ $maxkeysize -eq 256 ]]; then val=16 enctypes[${#enctypes[@]}]=aes256-cts-hmac-sha1-96 fi if [[ $minkeysize -eq 128 ]]; then ((val=val+8)) enctypes[${#enctypes[@]}]=aes128-cts-hmac-sha1-96 fi # RC4 comes next (whether it's better than 1DES or not -- AD prefers it) if encrypt -l|grep -q ^arcfour then ((val=val+4)) enctypes[${#enctypes[@]}]=arcfour-hmac-md5 else # Use 1DES ONLY if we don't have arcfour userAccountControl=$((userAccountControl + 2097152)) fi if encrypt -l | grep -q ^des then ((val=val+2)) enctypes[${#enctypes[@]}]=des-cbc-md5 fi if [[ ${#enctypes[@]} -eq 0 ]] then printf "$(gettext "No enctypes are supported").\n" printf "$(gettext "Please enable arcfour or 1DES, then re-join; see cryptoadm(8)").\n" >&2 error_message fi # If domain crontroller is Longhorn or above then set new supported # encryption type attributes. if [[ $level -gt 2 ]]; then cat > "$object" </dev/null 2>&1 if [[ $? -ne 0 ]]; then printf "$(gettext "Warning: Could not set the supported encryption type for computer account").\n" fi fi # We should probably check whether arcfour is available, and if not, # then set the 1DES only flag, but whatever, it's not likely NOT to be # available on S10/Nevada! # Reset userAccountControl # # NORMAL_ACCOUNT (512) | DONT_EXPIRE_PASSWORD (65536) | # TRUSTED_FOR_DELEGATION (524288) # # and possibly UseDesOnly (2097152) (see above) # cat > "$object" </dev/null 2>&1 if [[ $? -ne 0 ]]; then printf "$(gettext "ldapmodify failed to modify account attribute").\n" >&2 error_message fi # Setup a keytab file set -A args -- for enctype in "${enctypes[@]}" do args[${#args[@]}]=-e args[${#args[@]}]=$enctype done rm $new_keytab > /dev/null 2>&1 cat > "$object" </dev/null 2>&1 if [[ $? -ne 0 ]]; then printf "$(gettext "ldapmodify failed to modify account attribute").\n" >&2 error_message fi # # In Windows, unlike MIT based implementations we salt the keys with # the UPN, which is based on the host/string15@realm elements, not # with the individual SPN strings. # salt=host/${locase_nodename}.${domain}@${realm} skeys=(host/${fqdn}@${realm} nfs/${fqdn}@${realm} HTTP/${fqdn}@${realm}) skeys+=(root/${fqdn}@${realm} cifs/${fqdn}@${realm}) skeys+=(${netbios_nodename}@${realm} host/${upcase_nodename}@${realm}) skeys+=(cifs/${upcase_nodename}@${realm}) ks_args="-n -s $salt -v $kvno -k $new_keytab ${args[@]}" for skey in ${skeys[@]} do printf "%s" "$newpw" | $KSETPW $ks_args $skey > /dev/null 2>&1 if [[ $? -ne 0 ]] then printf "$(gettext "Failed to set password").\n" >&2 error_message fi done doKRB5config addDNSRR $dom setSMB $dom $dc printf -- "---------------------------------------------------\n" printf "$(gettext "Setup COMPLETE").\n\n" kdestroy -q 1>$TMP_FILE 2>&1 rm -f $TMP_FILE rm -rf $TMPDIR > /dev/null 2>&1 exit 0 } ########################### # Main section # ########################### # # Set the Kerberos config file and some default strings/files # KRB5_CONFIG_FILE=/etc/krb5/krb5.conf KRB5_KEYTAB_FILE=/etc/krb5/krb5.keytab RESOLV_CONF_FILE=/etc/resolv.conf KLOOKUP=/usr/lib/krb5/klookup; check_bin $KLOOKUP KSETPW=/usr/lib/krb5/ksetpw; check_bin $KSETPW KSMB=/usr/lib/krb5/ksmb; check_bin $KSMB KDYNDNS=/usr/lib/krb5/kdyndns; check_bin $KDYNDNS KCONF=/usr/lib/krb5/kconf; check_bin $KCONF dns_lookup=no ask_fqdns=no adddns=no no_keytab=no checkval="" profile="" typeset -u realm typeset -l hostname KDC export TMPDIR="/var/run/kclient" mkdir $TMPDIR > /dev/null 2>&1 if [[ $? -ne 0 ]]; then printf "\n$(gettext "Can not create directory: %s")\n\n" $TMPDIR >&2 exit 1 fi TMP_FILE=$(mktemp -q -t kclient-tmpfile.XXXXXX) export KRB5_CONFIG=$(mktemp -q -t kclient-krb5conf.XXXXXX) export KRB5CCNAME=$(mktemp -q -t kclient-krb5ccache.XXXXXX) new_keytab=$(mktemp -q -t kclient-krb5keytab.XXXXXX) if [[ -z $TMP_FILE || -z $KRB5_CONFIG || -z $KRB5CCNAME || -z $new_keytab ]] then printf "\n$(gettext "Can not create temporary files, exiting").\n\n" >&2 exit 1 fi # # If we are interrupted, cleanup after ourselves # trap "exiting 1" HUP INT QUIT TERM if [[ -d /usr/bin ]]; then if [[ -d /usr/sbin ]]; then PATH=/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:$PATH export PATH else printf "\n$(gettext "Directory /usr/sbin not found, exiting").\n" >&2 exit 1 fi else printf "\n$(gettext "Directory /usr/bin not found, exiting").\n" >&2 exit 1 fi printf "\n$(gettext "Starting client setup")\n\n" printf -- "---------------------------------------------------\n" # # Check for uid 0, disallow otherwise # id 1>$TMP_FILE 2>&1 if [[ $? -eq 0 ]]; then if egrep -s "uid=0\(root\)" $TMP_FILE; then # uid is 0, go ahead ... : else printf "\n$(gettext "Administrative privileges are required to run this script, exiting").\n" >&2 error_message fi else cat $TMP_FILE; printf "\n$(gettext "uid check failed, exiting").\n" >&2 error_message fi uname=$(uname -n) hostname=${uname%%.*} # # Process the command-line arguments (if any) # OPTIND=1 while getopts nD:Kp:R:k:a:c:d:f:h:m:s:T: OPTIONS do case $OPTIONS in D) options="$options -D" domain_list="$OPTARG" ;; K) options="$options -K" no_keytab=yes ;; R) options="$options -R" realm="$OPTARG" checkval="REALM"; check_value $realm ;; T) options="$options -T" type="$OPTARG" if [[ $type == ms_ad ]]; then msad=yes adddns=yes else non_solaris=yes no_keytab=yes fi ;; a) options="$options -a" ADMIN_PRINC="$OPTARG" checkval="ADMIN_PRINC"; check_value $ADMIN_PRINC ;; c) options="$options -c" filepath="$OPTARG" ;; d) options="$options -d" dnsarg="$OPTARG" checkval="DNS_OPTIONS"; check_value $dnsarg ;; f) options="$options -f" fqdnlist="$OPTARG" ;; h) options="$options -h" logical_hn="$OPTARG" checkval="LOGICAL_HOSTNAME"; check_value $logical_hn ;; k) options="$options -k" kdc_list="$OPTARG" ;; m) options="$options -m" KDC="$OPTARG" checkval="KDC"; check_value $KDC ;; n) options="$options -n" add_nfs=yes ;; p) options="$options -p" profile="$OPTARG" read_profile $profile ;; s) options="$options -s" svc_list="$OPTARG" SVCs=${svc_list//,/ } ;; \?) usage ;; *) usage ;; esac done #correct argument count after options shift `expr $OPTIND - 1` if [[ -z $options ]]; then : else if [[ $# -ne 0 ]]; then usage fi fi # # Check to see if we will be a client of a MIT, Heimdal, Shishi, etc. # if [[ -z $options ]]; then query "$(gettext "Is this a client of a non-Solaris KDC") ?" non_solaris=$answer if [[ $non_solaris == yes ]]; then printf "$(gettext "Which type of KDC is the server"):\n" printf "\t$(gettext "ms_ad: Microsoft Active Directory")\n" printf "\t$(gettext "mit: MIT KDC server")\n" printf "\t$(gettext "heimdal: Heimdal KDC server")\n" printf "\t$(gettext "shishi: Shishi KDC server")\n" printf "$(gettext "Enter required KDC type"): " read kdctype if [[ $kdctype == ms_ad ]]; then msad=yes elif [[ $kdctype == mit || $kdctype == heimdal || \ $kdctype == shishi ]]; then no_keytab=yes else printf "\n$(gettext "Invalid KDC type option, valid types are ms_ad, mit, heimdal, or shishi, exiting").\n" >&2 error_message fi fi fi [[ $msad == yes ]] && join_domain # # Check for /etc/resolv.conf # if [[ -r $RESOLV_CONF_FILE ]]; then client_machine=`$KLOOKUP` if [[ $? -ne 0 ]]; then if [[ $adddns == no ]]; then printf "\n$(gettext "%s does not have a DNS record and is required for Kerberos setup")\n" $hostname >&2 error_message fi else # # If client entry already exists then do not recreate it # adddns=no hostname=${client_machine%%.*} domain=${client_machine#*.} fi short_fqdn=${domain#*.*} short_fqdn=$(echo $short_fqdn | grep "\.") else # # /etc/resolv.conf not present, exit ... # printf "\n$(gettext "%s does not exist and is required for Kerberos setup")\n" $RESOLV_CONF_FILE >&2 printf "$(gettext "Refer to resolv.conf(5), exiting").\n" >&2 error_message fi check_nss_conf || printf "$(gettext "/etc/nsswitch.conf does not make use of DNS for hosts and/or ipnodes").\n" [[ -n $fqdnlist ]] && verify_fqdnlist "$fqdnlist" if [[ -z $dnsarg && (-z $options || -z $filepath) ]]; then query "$(gettext "Do you want to use DNS for kerberos lookups") ?" if [[ $answer == yes ]]; then printf "\n$(gettext "Valid DNS lookup options are dns_lookup_kdc, dns_lookup_realm,\nand dns_fallback. Refer krb5.conf(5) for further details").\n" printf "\n$(gettext "Enter required DNS option"): " read dnsarg checkval="DNS_OPTIONS"; check_value $dnsarg set_dns_value $dnsarg fi else [[ -z $dnsarg ]] && dnsarg=none set_dns_value $dnsarg fi if [[ -n $kdc_list ]]; then if [[ -z $KDC ]]; then for kdc in $kdc_list; do break done KDC="$kdc" fi fi if [[ -z $realm ]]; then printf "$(gettext "Enter the Kerberos realm"): " read realm checkval="REALM"; check_value $realm fi if [[ -z $KDC ]]; then printf "$(gettext "Specify the master KDC hostname for the above realm"): " read KDC checkval="KDC"; check_value $KDC fi FKDC=`$KLOOKUP $KDC` # # Ping to see if the kdc is alive ! # ping_check $FKDC "KDC" if [[ -z $kdc_list && (-z $options || -z $filepath) ]]; then query "$(gettext "Do you have any slave KDC(s)") ?" if [[ $answer == yes ]]; then printf "$(gettext "Enter a comma-separated list of slave KDC host names"): " read kdc_list fi fi [[ -n $kdc_list ]] && verify_kdcs "$kdc_list" # # Check to see if we will have a dynamic presence in the realm # if [[ -z $options ]]; then query "$(gettext "Will this client need service keys") ?" if [[ $answer == no ]]; then no_keytab=yes fi fi # # Check to see if we are configuring the client to use a logical host name # of a cluster environment # if [[ -z $options ]]; then query "$(gettext "Is this client a member of a cluster that uses a logical host name") ?" if [[ $answer == yes ]]; then printf "$(gettext "Specify the logical hostname of the cluster"): " read logical_hn checkval="LOGICAL_HOSTNAME"; check_value $logical_hn setup_lhn fi fi if [[ -n $domain_list && (-z $options || -z $filepath) ]]; then query "$(gettext "Do you have multiple domains/hosts to map to realm %s" ) ?" $realm if [[ $answer == yes ]]; then printf "$(gettext "Enter a comma-separated list of domain/hosts to map to the default realm"): " read domain_list fi fi [[ -n domain_list ]] && domain_list=${domain_list//,/ } # # Start writing up the krb5.conf file, save the existing one # if already present # writeup_krb5_conf # # Is this client going to use krb-nfs? If so then we need to at least # uncomment the krb5* sec flavors in nfssec.conf. # if [[ -z $options ]]; then query "$(gettext "Do you plan on doing Kerberized nfs") ?" add_nfs=$answer fi if [[ $add_nfs == yes ]]; then modify_nfssec_conf # # We also want to enable gss as we now live in a SBD world # svcadm enable svc:/network/rpc/gss:default [[ $? -ne 0 ]] && printf "$(gettext "Warning: could not enable gss service").\n" fi if [[ -z $options ]]; then query "$(gettext "Do you want to update /etc/pam.conf") ?" if [[ $answer == yes ]]; then printf "$(gettext "Enter a list of PAM service names in the following format: service:{first|only|optional}[,..]"): " read svc_list SVCs=${svc_list//,/ } fi fi [[ -n $svc_list ]] && update_pam_conf # # Copy over krb5.conf master copy from filepath # if [[ -z $options || -z $filepath ]]; then query "$(gettext "Do you want to copy over the master krb5.conf file") ?" if [[ $answer == yes ]]; then printf "$(gettext "Enter the pathname of the file to be copied"): " read filepath fi fi if [[ -n $filepath && -r $filepath ]]; then cp $filepath $KRB5_CONFIG if [[ $? -eq 0 ]]; then printf "$(gettext "Copied %s to %s").\n" $filepath $KRB5_CONFIG else printf "$(gettext "Copy of %s failed, exiting").\n" $filepath >&2 error_message fi elif [[ -n $filepath ]]; then printf "\n$(gettext "%s not found, exiting").\n" $filepath >&2 error_message fi doKRB5config # # Populate any service keys needed for the client in the keytab file # if [[ $no_keytab != yes ]]; then setup_keytab else printf "\n$(gettext "Note: %s file not created, please refer to verify_ap_req_nofail in krb5.conf(5) for the implications").\n" $KRB5_KEYTAB_FILE printf "$(gettext "Client will also not be able to host services that use Kerberos").\n" fi printf -- "\n---------------------------------------------------\n" printf "$(gettext "Setup COMPLETE").\n\n" # # If we have configured the client in a cluster we need to remind the user # to propagate the keytab and configuration files to the other members. # if [[ -n $logical_hn ]]; then printf "\n$(gettext "Note, you will need to securely transfer the /etc/krb5/krb5.keytab and /etc/krb5/krb5.conf files to all the other members of your cluster").\n" fi # # Cleanup. # kdestroy -q 1>$TMP_FILE 2>&1 rm -f $TMP_FILE rm -rf $TMPDIR > /dev/null 2>&1 exit 0 /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include #include #include struct profile_string_list { char **list; int num; int max; }; /* * From prof_get.c as the following four functions are private in mech_krb5. */ /* * Initialize the string list abstraction. */ static errcode_t init_list(struct profile_string_list *list) { list->num = 0; list->max = 10; list->list = malloc(list->max * sizeof (char *)); if (list->list == NULL) return (ENOMEM); list->list[0] = NULL; return (0); } /* * If re_list is non-NULL then pass the list header to the caller else free * the previously allocated list. */ static void end_list(struct profile_string_list *list, char ***ret_list) { if (list == NULL) return; if (ret_list) { *ret_list = list->list; return; } else profile_free_list(list->list); list->num = list->max = 0; list->list = NULL; } /* * Add a string to the list. */ static errcode_t add_to_list(struct profile_string_list *list, const char *str) { char *newstr, **newlist; int newmax; if (list->num + 1 >= list->max) { newmax = list->max + 10; newlist = realloc(list->list, newmax * sizeof (char *)); if (newlist == NULL) return (ENOMEM); list->max = newmax; list->list = newlist; } newstr = strdup(str); if (newstr == NULL) return (ENOMEM); list->list[list->num++] = newstr; list->list[list->num] = NULL; return (0); } static void usage() { (void) fprintf(stderr, gettext("kconf -f -r " "-k -m \n -p " "-d \n")); exit(1); } int main(int argc, char **argv) { profile_t profile; errcode_t code; int c; char *realm, *kdcs, *master, *domain, *token, *lasts; char *file, **ret_values = NULL; boolean_t set_change = FALSE; struct profile_string_list values; file = NULL; domain = NULL; master = NULL; kdcs = NULL; realm = NULL; (void) setlocale(LC_ALL, ""); #if !defined(TEXT_DOMAIN) #define TEXT_DOMAIN "SYS_TEST" #endif /* TEXT_DOMAIN */ (void) textdomain(TEXT_DOMAIN); /* * kconf -f -r -k -m * -p -d */ while ((c = getopt(argc, argv, "f:r:k:a:s:p:d:m:")) != -1) { switch (c) { case 'f': file = optarg; break; case 'r': realm = optarg; break; case 'k': kdcs = optarg; break; case 'm': master = optarg; break; case 'p': if (strcmp(optarg, "SET_CHANGE") == 0) set_change = TRUE; break; case 'd': domain = optarg; break; default: usage(); break; } } code = __profile_init(file, &profile); if (code != 0) { fprintf(stderr, gettext("Wasn't able to initialize profile\n")); exit(code); } if (code = init_list(&values)) { fprintf(stderr, gettext("Can not initialize list %d\n"), code); goto error; } token = strtok_r(kdcs, ",", &lasts); do { if (token != NULL) { code = add_to_list(&values, token); if (code != 0) { fprintf(stderr, gettext("Can not add to list " "%d\n"), code); goto error; } } else { fprintf(stderr, gettext("Couldn't parse kdc list %d\n"), code); goto error; } } while ((token = strtok_r(NULL, ",", &lasts)) != NULL); end_list(&values, &ret_values); code = __profile_add_realm(profile, realm, master, ret_values, set_change, TRUE); if (code != 0) { fprintf(stderr, gettext("Wasn't able to add realm " "information\n")); goto error; } code = __profile_add_domain_mapping(profile, domain, realm); if (code != 0) { fprintf(stderr, gettext("Wasn't able to add domain mapping\n")); goto error; } error: if (ret_values != NULL) profile_free_list(ret_values); /* * Release profile, which will subsequently flush new profile to file. * If this fails then at least free profile memory. */ if ((code = __profile_release(profile)) != 0) __profile_abandon(profile); return (code); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include char *whoami = NULL; static void usage(); static void usage() { fprintf(stderr, gettext("Usage: %s -d fqdn\n"), whoami); fprintf(stderr, gettext("\t-d\tThe fully qualified domain of the client\n")); exit(1); } int main(int argc, char **argv) { char fqdn[MAXHOSTNAMELEN]; int c, ret = 0; (void) setlocale(LC_ALL, ""); #if !defined(TEXT_DOMAIN) #define TEXT_DOMAIN "SYS_TEST" #endif /* TEXT_DOMAIN */ (void) textdomain(TEXT_DOMAIN); whoami = argv[0]; while ((c = getopt(argc, argv, "d:")) != -1) { switch (c) { case 'd': (void) strncpy(fqdn, optarg, sizeof (fqdn)); break; default: usage(); break; } } if (argc != optind) usage(); /* * Update DNS RR for the client using DynDNS. First it tries the * unauthed version then it tries the GSS version. */ ret = dyndns_update(fqdn); return (ret); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Portions Copyright 2021, Chris Fraire . */ #include #include #include #include #include #include "k5-int.h" #define QUOTE(x) #x #define VAL2STR(x) QUOTE(x) static char *whoami = NULL; static void kt_add_entry(krb5_context ctx, krb5_keytab kt, const krb5_principal princ, const krb5_principal sprinc, krb5_enctype enctype, krb5_kvno kvno, const char *pw); static krb5_error_code kt_remove_entries(krb5_context ctx, krb5_keytab kt, const krb5_principal princ); static void usage(); int main(int argc, char **argv) { krb5_context ctx = NULL; krb5_error_code code = 0; krb5_enctype *enctypes = NULL; int enctype_count = 0; krb5_ccache cc = NULL; krb5_keytab kt = NULL; krb5_kvno kvno = 1; krb5_principal victim, salt = NULL; char *vprincstr, *ktname, *token, *lasts, *newpw; int c, result_code, i, len, nflag = 0; krb5_data result_code_string, result_string; (void) setlocale(LC_ALL, ""); #if !defined(TEXT_DOMAIN) #define TEXT_DOMAIN "SYS_TEST" #endif /* TEXT_DOMAIN */ (void) textdomain(TEXT_DOMAIN); /* Misc init stuff */ (void) memset(&result_code_string, 0, sizeof (result_code_string)); (void) memset(&result_string, 0, sizeof (result_string)); whoami = argv[0]; code = krb5_init_context(&ctx); if (code != 0) { com_err(whoami, code, gettext("krb5_init_context() failed")); exit(1); } while ((c = getopt(argc, argv, "v:c:k:e:ns:")) != -1) { switch (c) { case 'n': nflag++; break; case 'k': if (kt != NULL) usage(); len = snprintf(NULL, 0, "WRFILE:%s", optarg) + 1; if ((ktname = malloc(len)) == NULL) { (void) fprintf(stderr, gettext("Couldn't allocate memory\n")); exit(1); } (void) snprintf(ktname, len, "WRFILE:%s", optarg); if ((code = krb5_kt_resolve(ctx, ktname, &kt)) != 0) { com_err(whoami, code, gettext("Couldn't open/create " "keytab %s"), optarg); exit(1); } break; case 'c': if (cc != NULL) usage(); if ((code = krb5_cc_resolve(ctx, optarg, &cc)) != 0) { com_err(whoami, code, gettext("Couldn't open ccache %s"), optarg); exit(1); } break; case 'e': len = strlen(optarg); token = strtok_r(optarg, ",\t ", &lasts); if (token == NULL) usage(); do { if (enctype_count++ == 0) { enctypes = malloc(sizeof (*enctypes)); } else { enctypes = realloc(enctypes, sizeof (*enctypes) * enctype_count); } if (enctypes == NULL) { (void) fprintf(stderr, gettext ("Couldn't allocate memory")); exit(1); } code = krb5_string_to_enctype(token, &enctypes[enctype_count - 1]); if (code != 0) { com_err(whoami, code, gettext("Unknown " "or unsupported enctype %s"), optarg); exit(1); } } while ((token = strtok_r(NULL, ",\t ", &lasts)) != NULL); break; case 'v': kvno = (krb5_kvno) atoi(optarg); break; case 's': vprincstr = optarg; code = krb5_parse_name(ctx, vprincstr, &salt); if (code != 0) { com_err(whoami, code, gettext("krb5_parse_name(%s) failed"), vprincstr); exit(1); } break; default: usage(); break; } } if (nflag && enctype_count == 0) usage(); if (nflag == 0 && cc == NULL && (code = krb5_cc_default(ctx, &cc)) != 0) { com_err(whoami, code, gettext("Could not find a ccache")); exit(1); } if (enctype_count > 0 && kt == NULL && (code = krb5_kt_default(ctx, &kt)) != 0) { com_err(whoami, code, gettext("No keytab specified")); exit(1); } if (argc != (optind + 1)) usage(); vprincstr = argv[optind]; code = krb5_parse_name(ctx, vprincstr, &victim); if (code != 0) { com_err(whoami, code, gettext("krb5_parse_name(%s) failed"), vprincstr); exit(1); } if (!isatty(fileno(stdin))) { char buf[PASS_MAX + 1]; if (scanf("%" VAL2STR(PASS_MAX) "s", &buf) != 1) { (void) fprintf(stderr, gettext("Couldn't read new password\n")); exit(1); } newpw = strdup(buf); if (newpw == NULL) { (void) fprintf(stderr, gettext("Couldn't allocate memory\n")); exit(1); } } else { newpw = getpassphrase(gettext("Enter new password: ")); if (newpw == NULL) { (void) fprintf(stderr, gettext("Couldn't read new password\n")); exit(1); } newpw = strdup(newpw); if (newpw == NULL) { (void) fprintf(stderr, gettext("Couldn't allocate memory\n")); exit(1); } } if (nflag == 0) { code = krb5_set_password_using_ccache(ctx, cc, newpw, victim, &result_code, &result_code_string, &result_string); if (code != 0) { com_err(whoami, code, gettext("krb5_set_password() failed")); exit(1); } krb5_cc_close(ctx, cc); (void) printf("Result: %.*s (%d) %.*s\n", result_code == 0 ? strlen("success") : result_code_string.length, result_code == 0 ? "success" : result_code_string.data, result_code, result_string.length, result_string.data); if (result_code != 0) { (void) fprintf(stderr, gettext("Exiting...\n")); exit(result_code); } } if (enctype_count && (code = kt_remove_entries(ctx, kt, victim))) goto error; if (salt == NULL) salt = victim; for (i = 0; i < enctype_count; i++) kt_add_entry(ctx, kt, victim, salt, enctypes[i], kvno, newpw); error: if (kt != NULL) krb5_kt_close(ctx, kt); return (code ? 1 : 0); } static krb5_error_code kt_remove_entries(krb5_context ctx, krb5_keytab kt, const krb5_principal princ) { krb5_error_code code; krb5_kt_cursor cursor; krb5_keytab_entry entry; /* * This is not a fatal error, we expect this to fail in the majority * of cases (when clients are first initialized). */ code = krb5_kt_get_entry(ctx, kt, princ, 0, 0, &entry); if (code != 0) { com_err(whoami, code, gettext("Could not retrieve entry in keytab")); return (0); } krb5_kt_free_entry(ctx, &entry); code = krb5_kt_start_seq_get(ctx, kt, &cursor); if (code != 0) { com_err(whoami, code, gettext("While starting keytab scan")); return (code); } while ((code = krb5_kt_next_entry(ctx, kt, &entry, &cursor)) == 0) { if (krb5_principal_compare(ctx, princ, entry.principal)) { code = krb5_kt_end_seq_get(ctx, kt, &cursor); if (code != 0) { com_err(whoami, code, gettext("While temporarily " "ending keytab scan")); return (code); } code = krb5_kt_remove_entry(ctx, kt, &entry); if (code != 0) { com_err(whoami, code, gettext("While deleting entry " "from keytab")); return (code); } code = krb5_kt_start_seq_get(ctx, kt, &cursor); if (code != 0) { com_err(whoami, code, gettext("While restarting keytab scan")); return (code); } } krb5_kt_free_entry(ctx, &entry); } if (code && code != KRB5_KT_END) { com_err(whoami, code, gettext("While scanning keytab")); return (code); } if ((code = krb5_kt_end_seq_get(ctx, kt, &cursor))) { com_err(whoami, code, gettext("While ending keytab scan")); return (code); } return (0); } static void kt_add_entry(krb5_context ctx, krb5_keytab kt, const krb5_principal princ, const krb5_principal sprinc, krb5_enctype enctype, krb5_kvno kvno, const char *pw) { krb5_keytab_entry *entry; krb5_data password, salt; krb5_keyblock key; krb5_error_code code; char enctype_name[100]; if ((code = krb5_enctype_to_string(enctype, enctype_name, sizeof (enctype_name)))) { com_err(whoami, code, gettext("Enctype %d has no name!"), enctype); return; } if ((entry = (krb5_keytab_entry *) malloc(sizeof (*entry))) == NULL) { (void) fprintf(stderr, gettext("Couldn't allocate memory")); return; } (void) memset((char *)entry, 0, sizeof (*entry)); password.length = strlen(pw); password.data = (char *)pw; if ((code = krb5_principal2salt(ctx, sprinc, &salt)) != 0) { com_err(whoami, code, gettext("Could not compute salt for %s"), enctype_name); return; } code = krb5_c_string_to_key(ctx, enctype, &password, &salt, &key); if (code != 0) { com_err(whoami, code, gettext("Could not convert to key for %s"), enctype_name); krb5_xfree(salt.data); return; } (void) memcpy(&entry->key, &key, sizeof (krb5_keyblock)); entry->vno = kvno; entry->principal = princ; if ((code = krb5_kt_add_entry(ctx, kt, entry)) != 0) { com_err(whoami, code, gettext("Could not add entry to keytab")); } } static void usage() { (void) fprintf(stderr, gettext("Usage: %s [-c ccache] [-k keytab] " "[-e enctype_list] [-s salt_name] [-n] princ\n"), whoami); (void) fprintf(stderr, gettext("\t-n\tDon't set the principal's password\n")); (void) fprintf(stderr, gettext("\tenctype_list is a comma or whitespace" " separated list\n")); (void) fprintf(stderr, gettext("\tIf -n is used then -k and -e must be " "used\n")); exit(1); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #define QUOTE(x) #x #define VAL2STR(x) QUOTE(x) char *whoami = NULL; static void usage(); static void usage() { fprintf(stderr, gettext("Usage: %s [ -d fqdn ] [ -s server ]\n"), whoami); fprintf(stderr, gettext("\t-d\tThe fully qualified domain of the client\n")); fprintf(stderr, gettext("\t-s\tThe domain controller to join\n")); fprintf(stderr, gettext("\tstdin is used to read in the password or \n")); fprintf(stderr, gettext("\tthe password is prompted for.\n")); exit(1); } int main(int argc, char **argv) { char fqdn[MAXHOSTNAMELEN], server[MAXHOSTNAMELEN]; char *newpw; int c, ret = 0; (void) setlocale(LC_ALL, ""); #if !defined(TEXT_DOMAIN) #define TEXT_DOMAIN "SYS_TEST" #endif /* TEXT_DOMAIN */ (void) textdomain(TEXT_DOMAIN); whoami = argv[0]; while ((c = getopt(argc, argv, "d:s:")) != -1) { switch (c) { case 'd': (void) strncpy(fqdn, optarg, sizeof (fqdn)); break; case 's': (void) strncpy(server, optarg, sizeof (server)); break; default: usage(); break; } } if (argc != optind) usage(); if (!isatty(fileno(stdin))) { char buf[PASS_MAX + 1]; if (scanf("%" VAL2STR(PASS_MAX) "s", &buf) != 1) { fprintf(stderr, gettext("Couldn't read new password\n")); exit(1); } newpw = strdup(buf); if (newpw == NULL) { fprintf(stderr, gettext("Couldn't allocate memory\n")); exit(1); } } else { newpw = getpassphrase(gettext("Enter new password: ")); if (newpw == NULL) { fprintf(stderr, gettext("Couldn't read new password\n")); exit(1); } newpw = strdup(newpw); if (newpw == NULL) { fprintf(stderr, gettext("Couldn't allocate memory\n")); exit(1); } } /* * Set the SMF properties for smb for later use. */ ret = smb_setdomainprops(fqdn, server, newpw); free(newpw); return (ret); } # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" # # PAM include module for Kerberos authentication foremost and then fall-back # to crypt authentication. # # Hammerhead: pam_dhkeys.so.1 line removed (NIS/SUN-DES key support gone). OTHER auth requisite pam_authtok_get.so.1 OTHER auth required pam_unix_cred.so.1 OTHER auth sufficient pam_krb5.so.1 OTHER auth required pam_unix_auth.so.1 # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" # # PAM include module for Kerberos authentication foremost and then fall-back # to crypt authentication. # # Hammerhead: pam_dhkeys.so.1 line removed (NIS/SUN-DES key support gone). OTHER auth requisite pam_authtok_get.so.1 OTHER auth required pam_unix_cred.so.1 OTHER auth binding pam_krb5.so.1 OTHER auth required pam_unix_auth.so.1 # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" # # PAM include module for Kerberos authentication foremost and then fall-back # to crypt authentication. # # Hammerhead: pam_dhkeys.so.1 line removed (NIS/SUN-DES key support gone). OTHER auth requisite pam_authtok_get.so.1 OTHER auth required pam_unix_cred.so.1 OTHER auth required pam_unix_auth.so.1 OTHER auth optional pam_krb5.so.1 # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Copyright (c) 2018, Joyent, Inc. PROG= kdcmgr KLPROG= klookup OBJS= klookup.o SSRCS= kdcmgr.sh CSRCS= $(OBJS:.o=.c) include ../../../Makefile.cmd POFILE= $(SSRCS:%.sh=%.po) CERRWARN += -Wno-implicit-function-declaration # not linted SMATCH=off LDLIBS += -lresolv -lnsl CLOBBERFILES += $(KLPROG) .KEEP_STATE: all: $(PROG) $(KLPROG) KRB5LIBKLPROG= $(KLPROG:%=$(KRB5LIB)/%) install: $(PROG) $(KRB5SBINPROG) $(KRB5LIBKLPROG) $(KLPROG): $(OBJS) $(LINK.c) $(OBJS) -o $@ $(LDLIBS) $(POST_PROCESS) clean: $(RM) $(OBJS) include ../../../Makefile.targ #!/usr/bin/ksh # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # # This command provides an simple interface to configure, destroy, and to obtain # the status of a master or slave Kerberos KDC server. # function usage { app=`basename $0` printf "\n$(gettext "Usage: %s [ -a admprincipal ] [ -e enctype ] [ -h ]")\n" $app printf "\t$(gettext "[ -p pwfile ] [ -r realm ] subcommand")\n\n" printf "\t$(gettext "-a: Create non-default admin principal.")\n" printf "\t$(gettext "-e: Encryption type used to encrypt the master key")\n" printf "\t$(gettext "-h: This help message.")\n" printf "\t$(gettext "-p: File that contains the admin principal and master key password.")\n" printf "\t$(gettext "-r: Set the default realm for this server.")\n\n" printf "\t$(gettext "where 'subcommand' is one of the following:")\n\n" printf "\t$(gettext "create [ master ]")\n" printf "\t$(gettext "create [ -m masterkdc ] slave")\n" printf "\t$(gettext "destroy")\n" printf "\t$(gettext "status")\n\n" cleanup 1 } function ask { # ask question, set global answer typeset question=$1 default_answer=$2 if [[ -z $default_answer ]]; then print "$question \c" else print "$question [$default_answer]: \c" fi read answer [ -z "$answer" ] && answer="$default_answer" } function yesno { typeset question="$1" # answer is a global set by ask answer= yn=`printf "$(gettext "y/n")"` y=`printf "$(gettext "y")"` n=`printf "$(gettext "n")"` yes=`printf "$(gettext "yes")"` no=`printf "$(gettext "no")"` while [[ -z $answer ]]; do ask "$question" $yn case $answer in $y|$yes) answer=yes;; $n|$no) answer=no;; *) answer=;; esac done } function query { yesno "$*" if [[ $answer = no ]]; then printf "\t$(gettext "No action performed").\n" fi } function cleanup { integer ret=$1 kdestroy -q -c $TMP_CCACHE 1>$TMP_FILE 2>&1 rm -f $TMP_FILE exit $ret } function error_message { printf "---------------------------------------------------\n" printf "$(gettext "Setup FAILED").\n\n" cleanup 1 } function check_bin { bin=$1 if [[ ! -x $bin ]]; then printf "$(gettext "Could not access/execute %s").\n" $bin error_message fi } function check_ret { integer ret=$1 prog=$2 if [[ $ret -ne 0 ]]; then printf "\n$(gettext "%s failed with return value %d, exiting").\n\n" $prog $ret error_message fi } function ok_to_proceed { yesno "$@" if [[ $answer = no ]]; then printf "\n$(gettext "Exiting, no action performed")\n\n" cleanup 0 fi } function check_value { typeset arg="$1" if [[ -z $arg ]]; then printf "\n$(gettext "No input obtained for %s, exiting").\n" $checkval error_message else echo "$arg">$TMP_FILE if egrep -s '[*$^#!]+' $TMP_FILE; then printf "\n$(gettext "Invalid input obtained for %s, exiting").\n" $checkval error_message fi fi } function setup_kdc_conf { printf "\n$(gettext "Setting up %s").\n" $KRB5_KDC_CONF if [[ -r $KRB5_KDC_CONF ]]; then cat $KRB5_KDC_CONF > $KRB5_KDC_CONF.sav cannot_create $KRB5_KDC_CONF.sav $? fi exec 3>$KRB5_KDC_CONF if [[ $? -ne 0 ]]; then printf "\n$(gettext "Cannot write to %s, exiting").\n" $KRB5_KDC_CONF error_message fi printf "\n[kdcdefaults]\n\tkdc_ports = 88,750\n\n" 1>&3 printf "[realms]\n\t$REALM = {\n" 1>&3 printf "\t\tprofile = $KRB5_KRB_CONF\n" 1>&3 printf "\t\tdatabase_name = $PRINCDB\n" 1>&3 printf "\t\tmaster_key_type = $ENCTYPE\n" 1>&3 printf "\t\tacl_file = $KADM5ACL\n" 1>&3 printf "\t\tkadmind_port = 749\n" 1>&3 printf "\t\tmax_life = 8h 0m 0s\n" 1>&3 printf "\t\tmax_renewable_life = 7d 0h 0m 0s\n" 1>&3 printf "\t\tdefault_principal_flags = +preauth\n" 1>&3 printf "\t\tsunw_dbprop_enable = true\n" 1>&3 if [[ $master = yes ]]; then printf "\t\tsunw_dbprop_master_ulogsize = 1000\n" 1>&3 fi if [[ $slave = yes ]]; then printf "\t\tsunw_dbprop_slave_poll = 2m\n" 1>&3 fi printf "\t}\n" 1>&3 } function setup_krb_conf { printf "\n$(gettext "Setting up %s").\n" $KRB5_KRB_CONF if [[ -r $KRB5_KRB_CONF ]]; then cat $KRB5_KRB_CONF > $KRB5_KRB_CONF.sav cannot_create $KRB5_KRB_CONF.sav $? fi exec 3>$KRB5_KRB_CONF if [[ $? -ne 0 ]]; then printf "\n$(gettext "Cannot write to %s, exiting").\n" $KRB5_KRB_CONF error_message fi printf "[libdefaults]\n" 1>&3 printf "\tdefault_realm = $REALM\n\n" 1>&3 printf "[realms]\n" 1>&3 printf "\t$REALM = {\n" 1>&3 if [[ $slave = yes ]]; then printf "\t\tkdc = $master_hn\n" 1>&3 fi printf "\t\tkdc = $fqhn\n" 1>&3 if [[ $master = yes ]]; then printf "\t\tadmin_server = $fqhn\n" 1>&3 else printf "\t\tadmin_server = $master_hn\n" 1>&3 fi printf "\t}\n\n" 1>&3 printf "[domain_realm]\n" 1>&3 printf "\t.$domain = $REALM\n\n" 1>&3 printf "[logging]\n" 1>&3 printf "\tdefault = FILE:/var/krb5/kdc.log\n" 1>&3 printf "\tkdc = FILE:/var/krb5/kdc.log\n" 1>&3 printf "\tkdc_rotate = {\n\t\tperiod = 1d\n\t\tversions = 10\n\t}\n\n" 1>&3 printf "[appdefaults]\n" 1>&3 printf "\tkinit = {\n\t\trenewable = true\n\t\tforwardable = true\n" 1>&3 printf "\t}\n" 1>&3 } function cannot_create { typeset filename="$1" typeset stat="$2" if [[ $stat -ne 0 ]]; then printf "\n$(gettext "Cannot create/edit %s, exiting").\n" $filename error_message fi } function check_admin { message=$1 if [[ -z $ADMIN_PRINC ]]; then printf "$message" read ADMIN_PRINC checkval="ADMIN_PRINC"; check_value $ADMIN_PRINC fi echo "$ADMIN_PRINC">$TMP_FILE if egrep -s '\/admin' $TMP_FILE; then # Already in "/admin" format, do nothing : else if egrep -s '\/' $TMP_FILE; then printf "\n$(gettext "Improper entry for krb5 admin principal, exiting").\n" error_message else ADMIN_PRINC=$(echo "$ADMIN_PRINC/admin") fi fi } function ping_check { typeset machine="$1" if $PING $machine > /dev/null 2>&1; then : else printf "\n$(gettext "%s %s is unreachable, exiting").\n" $string $machine error_message fi } function check_host { echo "$host">$TMP_FILE if egrep -s '[^.]\.[^.]+$' $TMP_FILE; then # do nothing, host is in fqhn format : else if egrep -s '\.+' $TMP_FILE; then printf "\n$(gettext "Improper format of host name: '%s'").\n" printf "$(gettext "Expecting the following format: 'somehost.example.com' or 'somehost', exiting").\n" error_message else # Attach fqdn to host, to get the Fully Qualified Domain # Name of the host requested host=$(echo "$host.$domain") fi fi # # Ping to see if the host is alive! # ping_check $host } function kill_daemons { # Kill daemons so they won't go into maintenance mode $SVCADM disable -s krb5kdc if [[ $? -ne 0 ]]; then printf "\n$(gettext "Error in disabling krb5kdc, exiting").\n" error_message fi $SVCADM disable -s kadmin if [[ $? -ne 0 ]]; then printf "\n$(gettext "Error in disabling kadmind, exiting").\n" error_message fi $SVCADM disable -s krb5_prop if [[ $? -ne 0 ]]; then printf "\n$(gettext "Error in disabling kpropd, exiting").\n" error_message fi # Make sure that none of the daemons outside of SMF are running either pkill kadmind if [[ $? -gt 1 ]]; then printf "\n$(gettext "Error in killing kadmind, exiting").\n" error_message fi pkill krb5kdc if [[ $? -gt 1 ]]; then printf "\n$(gettext "Error in killing krb5kdc, exiting").\n" error_message fi pkill kpropd if [[ $? -gt 1 ]]; then printf "\n$(gettext "Error in killing kpropd, exiting").\n" error_message fi } function setup_mkeytab { check_admin "\n$(gettext "Enter the krb5 administrative principal to be created"): \c" if [[ -z $PWFILE ]]; then echo $KADMINL -q "ank $ADMIN_PRINC" check_ret $? $KADMINL else cat $PWFILE $PWFILE | $KADMINL -q "ank $ADMIN_PRINC" > /dev/null 2>&1 check_ret $? $KADMINL fi $KADMINL -q "ank -randkey host/$fqhn" 1>$TMP_FILE 2>&1 check_ret $? $KADMINL $KADMINL -q "ktadd host/$fqhn" 1>$TMP_FILE 2>&1 check_ret $? $KADMINL } function setup_skeytab { check_admin "\n$(gettext "Enter the krb5 administrative principal to be used"): \c" printf "$(gettext "Obtaining TGT for %s") ...\n" $ADMIN_PRINC if [[ -z $PWFILE ]]; then kinit -c $TMP_CCACHE -S kadmin/$master_hn $ADMIN_PRINC check_ret $? kinit else cat $PWFILE | kinit -c $TMP_CCACHE -S kadmin/$master_hn \ $ADMIN_PRINC > /dev/null 2>&1 fi klist -c $TMP_CCACHE 1>$TMP_FILE 2>&1 if egrep -s "$(gettext "Valid starting")" $TMP_FILE && \ egrep -s "kadmin/$master_hn@$REALM" $TMP_FILE; then : else printf "\n$(gettext "kinit of %s failed, exiting").\n" $ADMIN_PRINC error_message fi $KADMIN -c $TMP_CCACHE -q "ank -randkey kiprop/$fqhn" 1>$TMP_FILE 2>&1 check_ret $? $KADMIN $KADMIN -c $TMP_CCACHE -q "ktadd kiprop/$fqhn" 1>$TMP_FILE 2>&1 check_ret $? $KADMIN $KADMIN -c $TMP_CCACHE -q "ank -randkey host/$fqhn" 1>$TMP_FILE 2>&1 check_ret $? $KADMIN $KADMIN -c $TMP_CCACHE -q "ktadd host/$fqhn" 1>$TMP_FILE 2>&1 check_ret $? $KADMIN kdestroy -q -c $TMP_CCACHE 1>$TMP_FILE 2>&1 check_ret $? $kdestroy } function setup_kadm5acl { printf "\n$(gettext "Setting up %s").\n" $KADM5ACL if [[ -r $KADM5ACL ]]; then cat $KADM5ACL > $KADM5ACL.sav cannot_create $KADM5ACL.sav $? fi exec 3>$KADM5ACL if [[ $? -ne 0 ]]; then printf "\n$(gettext "Cannot write to %s, exiting").\n" $KADM5ACL error_message fi if [[ $master = yes ]]; then printf "\n$ADMIN_PRINC@$REALM\t\tacmil\n" 1>&3 printf "\nkiprop/*@$REALM\t\tp\n" 1>&3 else printf "\n*/admin@___default_realm___\t\t*\n" 1>&3 fi } function setup_kpropdacl { printf "\n$(gettext "Setting up %s").\n\n" $KPROPACL if [[ -r $KPROPACL ]]; then cat $KPROPACL > $KPROPACL.sav cannot_create $KPROPACL.sav $? fi exec 3>$KPROPACL if [[ $? -ne 0 ]]; then printf "\n$(gettext "Cannot write to %s, exiting").\n" $KPROPACL error_message fi printf "\nhost/$master_hn@$REALM\n" 1>&3 } function setup_master { # create principal DB (KDB) if [[ -z $PWFILE ]]; then echo kdb5_util create check_ret $? kdb5_util else cat $PWFILE $PWFILE | kdb5_util create > /dev/null check_ret $? kdb5_util fi setup_mkeytab setup_kadm5acl $SVCADM enable -r -s krb5kdc $SVCADM enable -r -s kadmin } function setup_slave { integer count=1 setup_skeytab # Clear the kadm5acl, since the start methods look at this file # to see if the server has been configured as a master server setup_kadm5acl setup_kpropdacl $SVCADM enable -r -s krb5_prop # Wait for full propagation of the database, in some environments # this could take a few seconds while [[ ! -f /var/krb5/principal ]]; do if [[ count -gt $LOOPCNT ]]; then printf "\n$(gettext "Could not receive updates from the master").\n" error_message ((count = count + 1)) fi printf "$(gettext "Waiting for database from master")...\n" sleep $SLEEPTIME done # The database is propagated now we need to create the stash file if [[ -z $PWFILE ]]; then kdb5_util stash check_ret $? kdb5_util else cat $PWFILE | kdb5_util stash > /dev/null 2>&1 check_ret $? kdb5_util fi $SVCADM enable -r -s krb5kdc } function kdb5_destroy { typeset status=0 typeset arg= [[ -n $REALM ]] && arg="-r $REALM" printf "$(gettext "yes")\n" | kdb5_util $arg destroy > /dev/null 2>&1 status=$? [[ $status -eq 0 ]] && return $status # Could mean that the admin could have already removed part of the # configuration. Better to check to see if anything else should be # destroyed. We check by looking at any other stash files in /var/krb5 stashfiles=`ls $STASH` for stash in $stashfiles do realm=${stash#*.k5.} [[ -z $realm ]] && continue printf "$(gettext "Found non-default realm: %s")\n" $realm query "$(gettext "Do you wish to destroy realm"): $realm ?" if [[ $answer == yes ]]; then printf "$(gettext "yes")\n" | kdb5_util -r $realm destroy > /dev/null 2>&1 status=$? if [[ $status -ne 0 ]]; then printf "$(gettext "Could not destroy realm: %s")\n" $realm return $status fi else printf "$(gettext "%s will not be destroyed").\n" $realm status=0 fi done return $status } function destroy_kdc { typeset status # Check first to see if this is an existing KDC or server if [[ -f $KRB5KT || -f $PRINCDB || -f $OLDPRINCDB ]] then if [[ -z $PWFILE ]]; then printf "\n$(gettext "Some of the following files are present on this system"):\n" echo "\t$KRB5KT\n\t$PRINCDB\n\t$OLDPRINCDB\n\t$STASH\n" if [[ -z $d_option ]]; then printf "$(gettext "You must first run 'kdcmgr destroy' to remove all of these files before creating a KDC server").\n\n" cleanup 1 else ok_to_proceed "$(gettext "All of these files will be removed, okay to proceed?")" fi fi else if [[ -n $d_option ]]; then printf "\n$(gettext "No KDC related files exist, exiting").\n\n" cleanup 0 fi return fi kdb5_destroy status=$? rm -f $KRB5KT [[ $status -ne 0 ]] && cleanup 1 } function kadm5_acl_configed { if [[ -s $KADM5ACL ]]; then grep -v '^[ ]*#' $KADM5ACL | \ egrep '_default_realm_' > /dev/null 2>&1 if [[ $? -gt 0 ]]; then return 0 fi fi return 1 } function status_kdc { integer is_master=0 printf "\n$(gettext "KDC Status Information")\n" echo "--------------------------------------------" svcs -xv svc:/network/security/krb5kdc:default if kadm5_acl_configed; then is_master=1 printf "\n$(gettext "KDC Master Status Information")\n" echo "--------------------------------------------" svcs -xv svc:/network/security/kadmin:default else printf "\n$(gettext "KDC Slave Status Information")\n" echo "--------------------------------------------" svcs -xv svc:/network/security/krb5_prop:default fi printf "\n$(gettext "Transaction Log Information")\n" echo "--------------------------------------------" /usr/sbin/kproplog -h printf "$(gettext "Kerberos Related File Information")\n" echo "--------------------------------------------" printf "$(gettext "(will display any missing files below)")\n" FILELIST="$KRB5_KDC_CONF $KRB5_KRB_CONF $KADM5ACL $KRB5KT $PRINCDB " for file in $FILELIST; do if [[ ! -s $file ]]; then printf "$(gettext "%s not found").\n" $file fi done if [[ $is_master -eq 0 && ! -s $KPROPACL ]]; then printf "$(gettext "%s not found").\n" $KPROPACL fi test ! -s $STASH && printf "$(gettext "Stash file not found") (/var/krb5/.k5.*).\n" echo cleanup 0 } # Start of Main script typeset -u REALM typeset -l host typeset -l fqhn # Defaults KRB5_KDC_CONF=/etc/krb5/kdc.conf KRB5_KRB_CONF=/etc/krb5/krb5.conf KADM5ACL=/etc/krb5/kadm5.acl KPROPACL=/etc/krb5/kpropd.acl KRB5KT=/etc/krb5/krb5.keytab PRINCDB=/var/krb5/principal OLDPRINCDB=/var/krb5/principal.old STASH=/var/krb5/.k5.* KADMINL=/usr/sbin/kadmin.local; check_bin $KADMINL KADMIN=/usr/sbin/kadmin; check_bin $KADMIN KDCRES=/usr/lib/krb5/klookup; check_bin $KDCRES SVCADM=/sbin/svcadm; check_bin $SVCADM PING=/sbin/ping; check_bin $PING ENCTYPE=aes128-cts-hmac-sha1-96 LOOPCNT=10 SLEEPTIME=5 if [[ -x /usr/bin/mktemp ]]; then TMP_FILE=$(/usr/bin/mktemp /etc/krb5/krb5tmpfile.XXXXXX) TMP_CCACHE=$(/usr/bin/mktemp /etc/krb5/krb5tmpccache.XXXXXX) else TMP_FILE="/etc/krb5/krb5tmpfile.$$" TMP_CCACHE="/etc/krb5/krb5tmpccache.$$" fi if [[ ! -f /etc/resolv.conf ]]; then printf "$(gettext "Error: need to configure /etc/resolv.conf").\n" cleanup 1 fi fqhn=`$KDCRES` if [[ -n "$fqhn" ]]; then : elif [[ -n $(hostname) && -n $(domainname) ]]; then fqhn=$(hostname|cut -f1 -d'.').$(domainname|cut -f2- -d'.') else printf "$(gettext "Error: can not determine full hostname (FQHN). Aborting")\n" printf "$(gettext "Note, trying to use hostname and domainname to get FQHN").\n" cleanup 1 fi ping_check $fqhn domain=${fqhn#*.} # remove host part exitmsg=`printf "$(gettext "Exiting...")"` trap "echo $exitmsg; rm -f $TMP_FILE $TMP_CCACHE; exit 1" HUP INT QUIT TERM while getopts :a:e:hp:r:s flag do case "$flag" in a) ADMIN_PRINC=$OPTARG;; e) ENCTYPE=$OPTARG;; h) usage;; p) PWFILE=$OPTARG if [[ ! -r $PWFILE ]]; then printf "\n$(gettext "Password file %s does not exist, exiting").\n\n" $PWFILE cleanup 1 fi ;; r) REALM=$OPTARG;; *) usage;; esac done shift $(($OPTIND - 1)) case "$*" in create) master=yes;; "create master") master=yes;; "create -m "*) host=$3 checkval="MASTER"; check_value $host check_host master_hn=$host if [[ $4 != slave ]]; then usage fi;& "create slave") slave=yes;; destroy) d_option=yes kill_daemons destroy_kdc cleanup 0;; status) status_kdc;; *) usage;; esac kill_daemons printf "\n$(gettext "Starting server setup")\n" printf "---------------------------------------------------\n" # Checks for existing kdb and destroys if desired destroy_kdc if [[ -z $REALM ]]; then printf "$(gettext "Enter the Kerberos realm"): \c" read REALM checkval="REALM"; check_value $REALM fi if [[ -z $master && -z $slave ]]; then query "$(gettext "Is this machine to be configured as a master?"): \c" master=$answer if [[ $answer = no ]]; then query "$(gettext "Is this machine to be configured as a slave?"): \c" slave=$answer if [[ $answer = no ]]; then printf "\n$(gettext "Machine must either be a master or a slave KDC server").\n" error_message fi fi fi if [[ $slave = yes && -z $master_hn ]]; then printf "$(gettext "What is the master KDC's host name?"): \c" read host checkval="MASTER"; check_value $host check_host master_hn=$host fi setup_kdc_conf setup_krb_conf if [[ $master = yes ]]; then setup_master else setup_slave fi printf "\n---------------------------------------------------\n" printf "$(gettext "Setup COMPLETE").\n\n" cleanup 0 /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include /* * Private resolver of target and type with arguments: * klooukp [ target [ RR_type ] ] * * Utitilizes DNS lookups to discover domain and realm information. This CLI * is used primarily by kdcmgr(8) and kclient(8). */ int /* ARGSUSED */ main(int argc, char **argv) { unsigned char answer[NS_MAXMSG], *ansp = NULL, *end, a, b, c, d; int len = 0, anslen, hostlen, nq, na, type, class; int ttl __unused, priority __unused, weight __unused, port, size; char name[NS_MAXDNAME], *cp, *typestr = NULL; char nbuf[INET6_ADDRSTRLEN]; struct __res_state stat; int found = 0; int rr_type = T_A; HEADER *h; type = T_A; if (argc > 3) exit(1); if (argc == 1) { if (gethostname(name, MAXHOSTNAMELEN) != 0) exit(1); } else { (void) strncpy(name, (char *)argv[1], NS_MAXDNAME); if (argc == 3) { typestr = argv[2]; switch (*typestr) { case 'A': rr_type = T_A; break; case 'C': rr_type = T_CNAME; break; case 'I': rr_type = T_A; break; case 'P': rr_type = T_PTR; (void) sscanf(name, "%hhd.%hhd.%hhd.%hhd", &a, &b, &c, &d); (void) sprintf(name, "%d.%d.%d.%d.in-addr.arpa", d, c, b, a); break; case 'S': rr_type = T_SRV; break; default: exit(1); } } } (void) memset(&stat, 0, sizeof (stat)); if (res_ninit(&stat) == -1) exit(1); anslen = sizeof (answer); len = res_nsearch(&stat, name, C_IN, rr_type, answer, anslen); if (len < sizeof (HEADER)) { res_ndestroy(&stat); exit(1); } ansp = answer; end = ansp + anslen; /* LINTED */ h = (HEADER *)answer; nq = ntohs(h->qdcount); na = ntohs(h->ancount); ansp += HFIXEDSZ; if (nq != 1 || na < 1) { res_ndestroy(&stat); exit(1); } hostlen = sizeof (name); len = dn_expand(answer, end, ansp, name, hostlen); if (len < 0) { res_ndestroy(&stat); exit(1); } ansp += len + QFIXEDSZ; if (ansp > end) { res_ndestroy(&stat); exit(1); } while (na-- > 0 && ansp < end) { len = dn_expand(answer, end, ansp, name, hostlen); if (len < 0) continue; ansp += len; /* name */ NS_GET16(type, ansp); /* type */ NS_GET16(class, ansp); /* class */ NS_GET32(ttl, ansp); /* ttl */ NS_GET16(size, ansp); /* size */ if ((ansp + size) > end) { res_ndestroy(&stat); exit(1); } if (type == T_SRV) { NS_GET16(priority, ansp); NS_GET16(weight, ansp); NS_GET16(port, ansp); len = dn_expand(answer, end, ansp, name, hostlen); if (len < 0) { res_ndestroy(&stat); exit(1); } for (cp = name; *cp; cp++) { *cp = tolower(*cp); } (void) printf("%s %d\n", name, port); } else if (typestr && *typestr == 'I') { (void) inet_ntop(AF_INET, (void *)ansp, nbuf, INET6_ADDRSTRLEN); len = size; (void) printf("%s\n", nbuf); } else if (type == T_PTR) { len = dn_expand(answer, end, ansp, name, hostlen); if (len < 0) { res_ndestroy(&stat); exit(1); } } ansp += len; if (type == rr_type && class == C_IN) { found = 1; if (type != T_SRV && !(typestr && *typestr == 'I')) break; } } if (found != 1) { res_ndestroy(&stat); exit(1); } for (cp = name; *cp; cp++) { *cp = tolower(*cp); } if (type != T_SRV && !(typestr && *typestr == 'I')) (void) printf("%s\n", name); res_ndestroy(&stat); return (0); } # # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Copyright (c) 2018, Joyent, Inc. PROG= kpasswd OBJS = tty_kpasswd.o kpasswd.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 # # Override $ROOTLIB # KRB5LIBDIR= $(ROOT)$(KERBLIBDIR) CPPFLAGS += -I$(SRC)/lib/gss_mechs/mech_krb5/include \ -I$(SRC)/uts/common/gssapi/include/ \ -I$(SRC)/uts/common/gssapi/mechs/krb5/include \ -I$(SRC)/lib/krb5 \ -DHAVE_LIBSOCKET=1 -DHAVE_LIBNSL=1 -DHAVE_UNISTD_H=1 \ -DHAVE_SYS_TIMEB_H=1 -DHAVE_ALLOCA_H=1 -DHAVE_FTIME=1 \ -DHAVE_TIMEZONE -DUSE_KADM5_API_VERSION=2 CERRWARN += -Wno-parentheses CERRWARN += -Wno-unused-variable CERRWARN += -Wno-unused-function # not linted SMATCH=off LDFLAGS += $(KRUNPATH) $(KERBRUNPATH) LDLIBS += -L $(KRB5LIBDIR) LDLIBS += $(KMECHLIB) $(KCLNTLIB) .KEEP_STATE: all: $(PROG) $(PROG): $(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 2008 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 * */ /* * Copyright 1993-1994 OpenVision Technologies, Inc., All Rights Reserved. * * $Header$ * * */ static char rcsid[] = "$Id: kpasswd.c 17258 2005-06-21 01:36:03Z raeburn $"; #include #include #include "kpasswd_strings.h" #define string_text error_message #include "kpasswd.h" #include #include #include #include extern char *whoami; extern void display_intro_message(); extern long read_old_password(); extern long read_new_password(); #define MISC_EXIT_STATUS 6 /* * Function: kpasswd * * Purpose: Initialize and call lower level routines to change a password * * Arguments: * * context (r) krb5_context to use * argc/argv (r) principal name to use, optional * read_old_password (f) function to read old password * read_new_password (f) function to read new and change password * display_intro_message (f) function to display intro message * whoami (extern) argv[0] * * Returns: * exit status of 0 for success * 1 principal unknown * 2 old password wrong * 3 cannot initialize admin server session * 4 new passwd mismatch or error trying to change pw * 5 password not typed * 6 misc error * 7 incorrect usage * * Requires: * Passwords cannot be more than 255 characters long. * * Effects: * * If argc is 2, the password for the principal specified in argv[1] * is changed; otherwise, the principal of the default credential * cache or username is used. display_intro_message is called with * the arguments KPW_STR_CHANGING_PW_FOR and the principal name. * read_old_password is then called to prompt for the old password. * The admin system is then initialized, the principal's policy * retrieved and explained, if appropriate, and finally * read_new_password is called to read the new password and change the * principal's password (presumably ovsec_kadm_chpass_principal). * admin system is de-initialized before the function returns. * * Modifies: * * Changes the principal's password. * */ int kpasswd(context, argc, argv) krb5_context context; int argc; char *argv[]; { kadm5_ret_t code; krb5_ccache ccache = NULL; krb5_principal princ = 0; char *princ_str; struct passwd *pw = 0; unsigned int pwsize; char password[255]; /* I don't really like 255 but that's what kinit uses */ char msg_ret[1024], admin_realm[1024]; kadm5_principal_ent_rec principal_entry; kadm5_policy_ent_rec policy_entry; void *server_handle; kadm5_config_params params; char *cpw_service; memset((char *)¶ms, 0, sizeof (params)); memset(&principal_entry, 0, sizeof (principal_entry)); memset(&policy_entry, 0, sizeof (policy_entry)); if (argc > 2) { com_err(whoami, KPW_STR_USAGE, 0); return(7); /*NOTREACHED*/ } /************************************ * Get principal name to change * ************************************/ /* Look on the command line first, followed by the default credential cache, followed by defaulting to the Unix user name */ if (argc == 2) princ_str = strdup(argv[1]); else { code = krb5_cc_default(context, &ccache); /* If we succeed, find who is in the credential cache */ if (code == 0) { /* Get default principal from cache if one exists */ code = krb5_cc_get_principal(context, ccache, &princ); /* if we got a principal, unparse it, otherwise get out of the if with an error code */ (void) krb5_cc_close(context, ccache); if (code == 0) { code = krb5_unparse_name(context, princ, &princ_str); if (code != 0) { com_err(whoami, code, string_text(KPW_STR_UNPARSE_NAME)); return(MISC_EXIT_STATUS); } } } /* this is a crock.. we want to compare against */ /* "KRB5_CC_DOESNOTEXIST" but there is no such error code, and */ /* both the file and stdio types return FCC_NOFILE. If there is */ /* ever another ccache type (or if the error codes are ever */ /* fixed), this code will have to be updated. */ if (code && code != KRB5_FCC_NOFILE) { com_err(whoami, code, string_text(KPW_STR_WHILE_LOOKING_AT_CC)); return(MISC_EXIT_STATUS); } /* if either krb5_cc failed check the passwd file */ if (code != 0) { pw = getpwuid( getuid()); if (pw == NULL) { com_err(whoami, 0, string_text(KPW_STR_NOT_IN_PASSWD_FILE)); return(MISC_EXIT_STATUS); } princ_str = strdup(pw->pw_name); } } display_intro_message(string_text(KPW_STR_CHANGING_PW_FOR), princ_str); /* Need to get a krb5_principal, unless we started from with one from the credential cache */ if (! princ) { code = krb5_parse_name (context, princ_str, &princ); if (code != 0) { com_err(whoami, code, string_text(KPW_STR_PARSE_NAME), princ_str); free(princ_str); return(MISC_EXIT_STATUS); } } pwsize = sizeof(password); code = read_old_password(context, password, &pwsize); if (code != 0) { memset(password, 0, sizeof(password)); com_err(whoami, code, string_text(KPW_STR_WHILE_READING_PASSWORD)); krb5_free_principal(context, princ); free(princ_str); return(MISC_EXIT_STATUS); } if (pwsize == 0) { memset(password, 0, sizeof(password)); com_err(whoami, 0, string_text(KPW_STR_NO_PASSWORD_READ)); krb5_free_principal(context, princ); free(princ_str); return(5); } snprintf(admin_realm, sizeof (admin_realm), krb5_princ_realm(context, princ)->data); params.mask |= KADM5_CONFIG_REALM; params.realm = admin_realm; if (kadm5_get_cpw_host_srv_name(context, admin_realm, &cpw_service)) { fprintf(stderr, gettext("%s: unable to get host based " "service name for realm %s\n"), whoami, admin_realm); exit(1); } code = kadm5_init_with_password(princ_str, password, cpw_service, ¶ms, KADM5_STRUCT_VERSION, KADM5_API_VERSION_2, NULL, &server_handle); free(cpw_service); if (code != 0) { if (code == KADM5_BAD_PASSWORD) com_err(whoami, 0, string_text(KPW_STR_OLD_PASSWORD_INCORRECT)); else com_err(whoami, 0, string_text(KPW_STR_CANT_OPEN_ADMIN_SERVER), admin_realm, error_message(code)); krb5_free_principal(context, princ); free(princ_str); return ((code == KADM5_BAD_PASSWORD) ? 2 : 3); } /* * we can only check the policy if the server speaks * RPCSEC_GSS */ if (_kadm5_get_kpasswd_protocol(server_handle) == KRB5_CHGPWD_RPCSEC) { /* Explain policy restrictions on new password if any. */ /* * Note: copy of this exists in login * (kverify.c/get_verified_in_tkt). */ code = kadm5_get_principal(server_handle, princ, &principal_entry, KADM5_PRINCIPAL_NORMAL_MASK); if (code != 0) { com_err(whoami, 0, string_text((code == KADM5_UNK_PRINC) ? KPW_STR_PRIN_UNKNOWN : KPW_STR_CANT_GET_POLICY_INFO), princ_str); krb5_free_principal(context, princ); free(princ_str); (void) kadm5_destroy(server_handle); return ((code == KADM5_UNK_PRINC) ? 1 : MISC_EXIT_STATUS); } if ((principal_entry.aux_attributes & KADM5_POLICY) != 0) { code = kadm5_get_policy(server_handle, principal_entry.policy, &policy_entry); if (code != 0) { /* * doesn't matter which error comes back, * there's no nice recovery or need to * differentiate to the user */ com_err(whoami, 0, string_text(KPW_STR_CANT_GET_POLICY_INFO), princ_str); (void) kadm5_free_principal_ent(server_handle, &principal_entry); krb5_free_principal(context, princ); free(princ_str); free(princ_str); (void) kadm5_destroy(server_handle); return (MISC_EXIT_STATUS); } com_err(whoami, 0, string_text(KPW_STR_POLICY_EXPLANATION), princ_str, principal_entry.policy, policy_entry.pw_min_length, policy_entry.pw_min_classes); if (code = kadm5_free_principal_ent(server_handle, &principal_entry)) { (void) kadm5_free_policy_ent(server_handle, &policy_entry); krb5_free_principal(context, princ); free(princ_str); com_err(whoami, code, string_text(KPW_STR_WHILE_FREEING_PRINCIPAL)); (void) kadm5_destroy(server_handle); return (MISC_EXIT_STATUS); } if (code = kadm5_free_policy_ent(server_handle, &policy_entry)) { krb5_free_principal(context, princ); free(princ_str); com_err(whoami, code, string_text(KPW_STR_WHILE_FREEING_POLICY)); (void) kadm5_destroy(server_handle); return (MISC_EXIT_STATUS); } } else { /* * kpasswd *COULD* output something here to * encourage the choice of good passwords, * in the absence of an enforced policy. */ if (code = kadm5_free_principal_ent(server_handle, &principal_entry)) { krb5_free_principal(context, princ); free(princ_str); com_err(whoami, code, string_text(KPW_STR_WHILE_FREEING_PRINCIPAL)); (void) kadm5_destroy(server_handle); return (MISC_EXIT_STATUS); } } } /* if protocol == KRB5_CHGPWD_RPCSEC */ pwsize = sizeof(password); code = read_new_password(server_handle, password, &pwsize, msg_ret, sizeof (msg_ret), princ); memset(password, 0, sizeof(password)); if (code) com_err(whoami, 0, msg_ret); krb5_free_principal(context, princ); free(princ_str); (void) kadm5_destroy(server_handle); if (code == KRB5_LIBOS_CANTREADPWD) return(5); else if (code) return(4); else return(0); } /* * kadmin/passwd/kpasswd.h * * Copyright 2001 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. * * * Prototypes for the kpasswd program callback functions. */ #ifndef __KPASSWD_H__ #define __KPASSWD_H__ int kpasswd(krb5_context context, int argc, char *argv[]); long read_old_password(krb5_context context, char *password, unsigned int *pwsize); long read_new_password(void *server_handle, char *password, unsigned int *pwsize, char *msg_ret, int msg_len, krb5_principal princ); void display_intro_message(const char *fmt_string, const char *arg_string); #endif /* __KPASSWD_H__ */ /* * 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 * */ #include /* * kpasswd_strings.h: * This file is automatically generated; please do not edit it. */ #define KPW_STR_USAGE (-1767084800L) #define KPW_STR_PRIN_UNKNOWN (-1767084799L) #define KPW_STR_WHILE_LOOKING_AT_CC (-1767084798L) #define KPW_STR_OLD_PASSWORD_INCORRECT (-1767084797L) #define KPW_STR_CANT_OPEN_ADMIN_SERVER (-1767084796L) #define KPW_STR_NEW_PASSWORD_MISMATCH (-1767084795L) #define KPW_STR_PASSWORD_CHANGED (-1767084794L) #define KPW_STR_PASSWORD_NOT_CHANGED (-1767084793L) #define KPW_STR_PARSE_NAME (-1767084792L) #define KPW_STR_UNPARSE_NAME (-1767084791L) #define KPW_STR_NOT_IN_PASSWD_FILE (-1767084790L) #define KPW_STR_CHANGING_PW_FOR (-1767084789L) #define KPW_STR_OLD_PASSWORD_PROMPT (-1767084788L) #define KPW_STR_WHILE_READING_PASSWORD (-1767084787L) #define KPW_STR_NO_PASSWORD_READ (-1767084786L) #define KPW_STR_WHILE_TRYING_TO_CHANGE (-1767084785L) #define KPW_STR_WHILE_DESTROYING_ADMIN_SESSION (-1767084784L) #define KPW_STR_WHILE_FREEING_PRINCIPAL (-1767084783L) #define KPW_STR_WHILE_FREEING_POLICY (-1767084782L) #define KPW_STR_CANT_GET_POLICY_INFO (-1767084781L) #define KPW_STR_POLICY_EXPLANATION (-1767084780L) #define ERROR_TABLE_BASE_kpws (-1767084800L) extern const struct error_table et_kpws_error_table; #if !defined(_WIN32) /* for compatibility with older versions... */ extern void initialize_kpws_error_table (void) /*@modifies internalState@*/; #else #define initialize_kpws_error_table() #endif #if !defined(_WIN32) #define init_kpws_err_tbl initialize_kpws_error_table #define kpws_err_base ERROR_TABLE_BASE_kpws #endif /* * 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 * */ /* * Copyright 1993-1994 OpenVision Technologies, Inc., All Rights Reserved. * * $Header: /cvs/krbdev/krb5/src/kadmin/passwd/tty_kpasswd.c,v 1.9 2001/02/26 18:22:08 epeisach Exp $ * * */ static char rcsid[] = "$Id: tty_kpasswd.c,v 1.9 2001/02/26 18:22:08 epeisach Exp $"; #include #include #include "kpasswd_strings.h" #define string_text error_message #include "kpasswd.h" #include #include #include #include #include char *whoami; void display_intro_message(fmt_string, arg_string) const char *fmt_string; const char *arg_string; { com_err(whoami, 0, fmt_string, arg_string); } long read_old_password(context, password, pwsize) krb5_context context; char *password; unsigned int *pwsize; { long code = krb5_read_password(context, (char *) string_text(KPW_STR_OLD_PASSWORD_PROMPT), 0, password, pwsize); return code; } long read_new_password(server_handle, password, pwsize, msg_ret, msg_len, princ) void *server_handle; char *password; unsigned int *pwsize; char *msg_ret; int msg_len; krb5_principal princ; { return (kadm5_chpass_principal_util(server_handle, princ, NULL, NULL /* don't need new pw back */, msg_ret, msg_len)); } /* * main() for tty version of kpasswd.c */ int main(argc, argv) int argc; char *argv[]; { krb5_context context; int retval; whoami = (whoami = strrchr(argv[0], '/')) ? whoami + 1 : argv[0]; (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(&context); if (retval) { com_err(whoami, retval, gettext("initializing krb5 context")); exit(retval); } /* initialize_kpws_error_table(); SUNWresync121 */ retval = kpasswd(context, argc, argv); if (!retval) printf(string_text(KPW_STR_PASSWORD_CHANGED)); exit(retval); } # # 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 */ # # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Copyright (c) 2018, Joyent, Inc. PROG= kadmind MANIFEST= kadmin.xml OBJS = kadm_rpc_svc.o server_stubs.o ovsec_kadmd.o misc.o server_glue_v1.o \ ipropd_svc.o DERIVED_OBJS= iprop_xdr.o SRCS= $(OBJS:.o=.c) SRCS+= $(DERIVED_OBJS:.o=.c) # Definitions needed to rpcgen iprop-related files ISRC= iprop.h iprop_xdr.c KRB5IPROPDIR= $(SRC)/cmd/krb5/iprop CMD= grep -v "usr/src/cmd/krb5/iprop" > $@ CLOBBERFILES += $(TESTPROG) include ../../../Makefile.cmd include $(SRC)/lib/gss_mechs/mech_krb5/Makefile.mech_krb5 POFILE = $(PROG).po POFILES = generic.po ROOTMANIFESTDIR= $(ROOTSVCNETWORKSECURITY) DEFS = -DHAVE_LIBSOCKET=1 -DHAVE_LIBNSL=1 -DHAVE_COMPILE=1 -DHAVE_STEP=1 -DKRB5_KRB4_COMPAT CPPFLAGS += -I. -I$(SRC)/uts/common/gssapi/mechs/krb5/include \ -I$(SRC)/uts/common/gssapi \ -I$(SRC)/lib/krb5 \ -I$(SRC)/lib/krb5/kadm5 \ -I$(SRC)/lib/krb5/kadm5/srv \ -I$(SRC)/lib/gss_mechs/mech_krb5/include \ -I$(KRB5IPROPDIR) \ -I$(SRC)/uts/common/gssapi/include/ $(DEFS) CERRWARN += -Wno-implicit-function-declaration CERRWARN += -Wno-unused-variable CERRWARN += -Wno-parentheses CERRWARN += -Wno-unused-function # Hammerhead: Suppress xdr_longlong_t type mismatch warnings (iprop_xdr.c) CERRWARN += -Wno-incompatible-pointer-types # not linted SMATCH=off LDFLAGS += $(KRUNPATH) $(KERBRUNPATH) # Hammerhead: GNU ld needs rpath-link for transitive deps (libdyn, libkdb) via libkadm5srv LDFLAGS += -Wl,-rpath-link,$(ROOT)/usr/lib/krb5 LDLIBS += -lgss -L$(KRB5LIB) \ -lmech_krb5 -lkadm5srv -lkdb -lnsl -lresolv -lbsm -lpam -lsocket .KEEP_STATE: all: $(PROG) $(PROG): $(OBJS) $(DERIVED_OBJS) $(LINK.c) $(OBJS) $(DERIVED_OBJS) -o $@ $(LDLIBS) $(POST_PROCESS) # Hammerhead: pre-generated (rpcgen + GNU cpp truncation bug) # iprop.h and iprop_xdr.c are now committed source files. # Explicitly state the dependancy on iprop.h $(OBJS): iprop.h $(KRB5LIBPROG): FILEMODE = 0500 install: $(KRB5LIBPROG) $(ROOTSVCMETHOD) $(ROOTMANIFEST) check: $(CHKMANIFEST) clean: $(RM) $(OBJS) $(DERIVED_OBJS) @# Hammerhead: do not delete pre-generated $(ISRC) include ../../../Makefile.targ $(POFILE): $(DERIVED_FILES) .WAIT $(POFILES) $(RM) $@ $(CAT) $(POFILES) > $@ generic.po: FRC $(RM) messages.po $(XGETTEXT) $(XGETFLAGS) `$(GREP) -l gettext *.[ch]` $(SED) "/^domain/d" messages.po > $@ $(RM) messages.po FRC: /* * Please do not edit this file. * It was generated using rpcgen. */ #ifndef _IPROP_H_RPCGEN #define _IPROP_H_RPCGEN #include typedef struct { u_int utf8str_t_len; char *utf8str_t_val; } utf8str_t; typedef uint32_t kdb_sno_t; struct kdbe_time_t { uint32_t seconds; uint32_t useconds; }; typedef struct kdbe_time_t kdbe_time_t; struct kdbe_key_t { int32_t k_ver; int32_t k_kvno; struct { u_int k_enctype_len; int32_t *k_enctype_val; } k_enctype; struct { u_int k_contents_len; utf8str_t *k_contents_val; } k_contents; }; typedef struct kdbe_key_t kdbe_key_t; struct kdbe_data_t { int32_t k_magic; utf8str_t k_data; }; typedef struct kdbe_data_t kdbe_data_t; struct kdbe_princ_t { utf8str_t k_realm; struct { u_int k_components_len; kdbe_data_t *k_components_val; } k_components; int32_t k_nametype; }; typedef struct kdbe_princ_t kdbe_princ_t; struct kdbe_tl_t { int16_t tl_type; struct { u_int tl_data_len; char *tl_data_val; } tl_data; }; typedef struct kdbe_tl_t kdbe_tl_t; typedef struct { u_int kdbe_pw_hist_t_len; kdbe_key_t *kdbe_pw_hist_t_val; } kdbe_pw_hist_t; enum kdbe_attr_type_t { AT_ATTRFLAGS = 0, AT_MAX_LIFE = 1, AT_MAX_RENEW_LIFE = 2, AT_EXP = 3, AT_PW_EXP = 4, AT_LAST_SUCCESS = 5, AT_LAST_FAILED = 6, AT_FAIL_AUTH_COUNT = 7, AT_PRINC = 8, AT_KEYDATA = 9, AT_TL_DATA = 10, AT_LEN = 11, AT_MOD_PRINC = 12, AT_MOD_TIME = 13, AT_MOD_WHERE = 14, AT_PW_LAST_CHANGE = 15, AT_PW_POLICY = 16, AT_PW_POLICY_SWITCH = 17, AT_PW_HIST_KVNO = 18, AT_PW_HIST = 19 }; typedef enum kdbe_attr_type_t kdbe_attr_type_t; struct kdbe_val_t { kdbe_attr_type_t av_type; union { uint32_t av_attrflags; uint32_t av_max_life; uint32_t av_max_renew_life; uint32_t av_exp; uint32_t av_pw_exp; uint32_t av_last_success; uint32_t av_last_failed; uint32_t av_fail_auth_count; kdbe_princ_t av_princ; struct { u_int av_keydata_len; kdbe_key_t *av_keydata_val; } av_keydata; struct { u_int av_tldata_len; kdbe_tl_t *av_tldata_val; } av_tldata; int16_t av_len; uint32_t av_pw_last_change; kdbe_princ_t av_mod_princ; uint32_t av_mod_time; utf8str_t av_mod_where; utf8str_t av_pw_policy; bool_t av_pw_policy_switch; uint32_t av_pw_hist_kvno; struct { u_int av_pw_hist_len; kdbe_pw_hist_t *av_pw_hist_val; } av_pw_hist; struct { u_int av_extension_len; char *av_extension_val; } av_extension; } kdbe_val_t_u; }; typedef struct kdbe_val_t kdbe_val_t; typedef struct { u_int kdbe_t_len; kdbe_val_t *kdbe_t_val; } kdbe_t; struct kdb_incr_update_t { utf8str_t kdb_princ_name; kdb_sno_t kdb_entry_sno; kdbe_time_t kdb_time; kdbe_t kdb_update; bool_t kdb_deleted; bool_t kdb_commit; struct { u_int kdb_kdcs_seen_by_len; utf8str_t *kdb_kdcs_seen_by_val; } kdb_kdcs_seen_by; struct { u_int kdb_futures_len; char *kdb_futures_val; } kdb_futures; }; typedef struct kdb_incr_update_t kdb_incr_update_t; typedef struct { u_int kdb_ulog_t_len; kdb_incr_update_t *kdb_ulog_t_val; } kdb_ulog_t; enum update_status_t { UPDATE_OK = 0, UPDATE_ERROR = 1, UPDATE_FULL_RESYNC_NEEDED = 2, UPDATE_BUSY = 3, UPDATE_NIL = 4, UPDATE_PERM_DENIED = 5 }; typedef enum update_status_t update_status_t; struct kdb_last_t { kdb_sno_t last_sno; kdbe_time_t last_time; }; typedef struct kdb_last_t kdb_last_t; struct kdb_incr_result_t { kdb_last_t lastentry; kdb_ulog_t updates; update_status_t ret; }; typedef struct kdb_incr_result_t kdb_incr_result_t; struct kdb_fullresync_result_t { kdb_last_t lastentry; update_status_t ret; }; typedef struct kdb_fullresync_result_t kdb_fullresync_result_t; #define KRB5_IPROP_PROG 100423 #define KRB5_IPROP_VERS 1 #define IPROP_NULL 0 extern void * iprop_null_1(); #define IPROP_GET_UPDATES 1 extern kdb_incr_result_t * iprop_get_updates_1(); #define IPROP_FULL_RESYNC 2 extern kdb_fullresync_result_t * iprop_full_resync_1(); extern int krb5_iprop_prog_1_freeresult(); /* the xdr functions */ extern bool_t xdr_utf8str_t(); extern bool_t xdr_kdb_sno_t(); extern bool_t xdr_kdbe_time_t(); extern bool_t xdr_kdbe_key_t(); extern bool_t xdr_kdbe_data_t(); extern bool_t xdr_kdbe_princ_t(); extern bool_t xdr_kdbe_tl_t(); extern bool_t xdr_kdbe_pw_hist_t(); extern bool_t xdr_kdbe_attr_type_t(); extern bool_t xdr_kdbe_val_t(); extern bool_t xdr_kdbe_t(); extern bool_t xdr_kdb_incr_update_t(); extern bool_t xdr_kdb_ulog_t(); extern bool_t xdr_update_status_t(); extern bool_t xdr_kdb_last_t(); extern bool_t xdr_kdb_incr_result_t(); extern bool_t xdr_kdb_fullresync_result_t(); #endif /* !_IPROP_H_RPCGEN */ /* * Please do not edit this file. * It was generated using rpcgen. */ #include "iprop.h" #ifndef _KERNEL #include #endif /* !_KERNEL */ #include "iprop.h" bool_t xdr_int16_t(xdrs, objp) XDR *xdrs; int16_t *objp; { rpc_inline_t *buf __unused; if (!xdr_short(xdrs, objp)) return (FALSE); return (TRUE); } bool_t xdr_uint16_t(xdrs, objp) XDR *xdrs; uint16_t *objp; { rpc_inline_t *buf __unused; if (!xdr_u_short(xdrs, objp)) return (FALSE); return (TRUE); } bool_t xdr_int32_t(xdrs, objp) XDR *xdrs; int32_t *objp; { rpc_inline_t *buf __unused; if (!xdr_int(xdrs, objp)) return (FALSE); return (TRUE); } bool_t xdr_uint32_t(xdrs, objp) XDR *xdrs; uint32_t *objp; { rpc_inline_t *buf __unused; if (!xdr_u_int(xdrs, objp)) return (FALSE); return (TRUE); } bool_t xdr_int64_t(xdrs, objp) XDR *xdrs; int64_t *objp; { rpc_inline_t *buf __unused; if (!xdr_longlong_t(xdrs, objp)) return (FALSE); return (TRUE); } bool_t xdr_uint64_t(xdrs, objp) XDR *xdrs; uint64_t *objp; { rpc_inline_t *buf __unused; if (!xdr_u_longlong_t(xdrs, objp)) return (FALSE); return (TRUE); } bool_t xdr_utf8str_t(xdrs, objp) XDR *xdrs; utf8str_t *objp; { rpc_inline_t *buf __unused; if (!xdr_bytes(xdrs, (char **)&objp->utf8str_t_val, (u_int *) &objp->utf8str_t_len, ~0)) return (FALSE); return (TRUE); } bool_t xdr_kdb_sno_t(xdrs, objp) XDR *xdrs; kdb_sno_t *objp; { rpc_inline_t *buf __unused; if (!xdr_uint32_t(xdrs, objp)) return (FALSE); return (TRUE); } bool_t xdr_kdbe_time_t(xdrs, objp) XDR *xdrs; kdbe_time_t *objp; { rpc_inline_t *buf __unused; if (!xdr_uint32_t(xdrs, &objp->seconds)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->useconds)) return (FALSE); return (TRUE); } bool_t xdr_kdbe_key_t(xdrs, objp) XDR *xdrs; kdbe_key_t *objp; { rpc_inline_t *buf __unused; if (!xdr_int32_t(xdrs, &objp->k_ver)) return (FALSE); if (!xdr_int32_t(xdrs, &objp->k_kvno)) return (FALSE); if (!xdr_array(xdrs, (char **)&objp->k_enctype.k_enctype_val, (u_int *) &objp->k_enctype.k_enctype_len, ~0, sizeof (int32_t), (xdrproc_t)xdr_int32_t)) return (FALSE); if (!xdr_array(xdrs, (char **)&objp->k_contents.k_contents_val, (u_int *) &objp->k_contents.k_contents_len, ~0, sizeof (utf8str_t), (xdrproc_t)xdr_utf8str_t)) return (FALSE); return (TRUE); } bool_t xdr_kdbe_data_t(xdrs, objp) XDR *xdrs; kdbe_data_t *objp; { rpc_inline_t *buf __unused; if (!xdr_int32_t(xdrs, &objp->k_magic)) return (FALSE); if (!xdr_utf8str_t(xdrs, &objp->k_data)) return (FALSE); return (TRUE); } bool_t xdr_kdbe_princ_t(xdrs, objp) XDR *xdrs; kdbe_princ_t *objp; { rpc_inline_t *buf __unused; if (!xdr_utf8str_t(xdrs, &objp->k_realm)) return (FALSE); if (!xdr_array(xdrs, (char **)&objp->k_components.k_components_val, (u_int *) &objp->k_components.k_components_len, ~0, sizeof (kdbe_data_t), (xdrproc_t)xdr_kdbe_data_t)) return (FALSE); if (!xdr_int32_t(xdrs, &objp->k_nametype)) return (FALSE); return (TRUE); } bool_t xdr_kdbe_tl_t(xdrs, objp) XDR *xdrs; kdbe_tl_t *objp; { rpc_inline_t *buf __unused; if (!xdr_int16_t(xdrs, &objp->tl_type)) return (FALSE); if (!xdr_bytes(xdrs, (char **)&objp->tl_data.tl_data_val, (u_int *) &objp->tl_data.tl_data_len, ~0)) return (FALSE); return (TRUE); } bool_t xdr_kdbe_pw_hist_t(xdrs, objp) XDR *xdrs; kdbe_pw_hist_t *objp; { rpc_inline_t *buf __unused; if (!xdr_array(xdrs, (char **)&objp->kdbe_pw_hist_t_val, (u_int *) &objp->kdbe_pw_hist_t_len, ~0, sizeof (kdbe_key_t), (xdrproc_t)xdr_kdbe_key_t)) return (FALSE); return (TRUE); } bool_t xdr_kdbe_attr_type_t(xdrs, objp) XDR *xdrs; kdbe_attr_type_t *objp; { rpc_inline_t *buf __unused; if (!xdr_enum(xdrs, (enum_t *)objp)) return (FALSE); return (TRUE); } bool_t xdr_kdbe_val_t(xdrs, objp) XDR *xdrs; kdbe_val_t *objp; { rpc_inline_t *buf __unused; if (!xdr_kdbe_attr_type_t(xdrs, &objp->av_type)) return (FALSE); switch (objp->av_type) { case AT_ATTRFLAGS: if (!xdr_uint32_t(xdrs, &objp->kdbe_val_t_u.av_attrflags)) return (FALSE); break; case AT_MAX_LIFE: if (!xdr_uint32_t(xdrs, &objp->kdbe_val_t_u.av_max_life)) return (FALSE); break; case AT_MAX_RENEW_LIFE: if (!xdr_uint32_t(xdrs, &objp->kdbe_val_t_u.av_max_renew_life)) return (FALSE); break; case AT_EXP: if (!xdr_uint32_t(xdrs, &objp->kdbe_val_t_u.av_exp)) return (FALSE); break; case AT_PW_EXP: if (!xdr_uint32_t(xdrs, &objp->kdbe_val_t_u.av_pw_exp)) return (FALSE); break; case AT_LAST_SUCCESS: if (!xdr_uint32_t(xdrs, &objp->kdbe_val_t_u.av_last_success)) return (FALSE); break; case AT_LAST_FAILED: if (!xdr_uint32_t(xdrs, &objp->kdbe_val_t_u.av_last_failed)) return (FALSE); break; case AT_FAIL_AUTH_COUNT: if (!xdr_uint32_t(xdrs, &objp->kdbe_val_t_u.av_fail_auth_count)) return (FALSE); break; case AT_PRINC: if (!xdr_kdbe_princ_t(xdrs, &objp->kdbe_val_t_u.av_princ)) return (FALSE); break; case AT_KEYDATA: if (!xdr_array(xdrs, (char **)&objp->kdbe_val_t_u.av_keydata.av_keydata_val, (u_int *) &objp->kdbe_val_t_u.av_keydata.av_keydata_len, ~0, sizeof (kdbe_key_t), (xdrproc_t)xdr_kdbe_key_t)) return (FALSE); break; case AT_TL_DATA: if (!xdr_array(xdrs, (char **)&objp->kdbe_val_t_u.av_tldata.av_tldata_val, (u_int *) &objp->kdbe_val_t_u.av_tldata.av_tldata_len, ~0, sizeof (kdbe_tl_t), (xdrproc_t)xdr_kdbe_tl_t)) return (FALSE); break; case AT_LEN: if (!xdr_int16_t(xdrs, &objp->kdbe_val_t_u.av_len)) return (FALSE); break; case AT_PW_LAST_CHANGE: if (!xdr_uint32_t(xdrs, &objp->kdbe_val_t_u.av_pw_last_change)) return (FALSE); break; case AT_MOD_PRINC: if (!xdr_kdbe_princ_t(xdrs, &objp->kdbe_val_t_u.av_mod_princ)) return (FALSE); break; case AT_MOD_TIME: if (!xdr_uint32_t(xdrs, &objp->kdbe_val_t_u.av_mod_time)) return (FALSE); break; case AT_MOD_WHERE: if (!xdr_utf8str_t(xdrs, &objp->kdbe_val_t_u.av_mod_where)) return (FALSE); break; case AT_PW_POLICY: if (!xdr_utf8str_t(xdrs, &objp->kdbe_val_t_u.av_pw_policy)) return (FALSE); break; case AT_PW_POLICY_SWITCH: if (!xdr_bool(xdrs, &objp->kdbe_val_t_u.av_pw_policy_switch)) return (FALSE); break; case AT_PW_HIST_KVNO: if (!xdr_uint32_t(xdrs, &objp->kdbe_val_t_u.av_pw_hist_kvno)) return (FALSE); break; case AT_PW_HIST: if (!xdr_array(xdrs, (char **)&objp->kdbe_val_t_u.av_pw_hist.av_pw_hist_val, (u_int *) &objp->kdbe_val_t_u.av_pw_hist.av_pw_hist_len, ~0, sizeof (kdbe_pw_hist_t), (xdrproc_t)xdr_kdbe_pw_hist_t)) return (FALSE); break; default: if (!xdr_bytes(xdrs, (char **)&objp->kdbe_val_t_u.av_extension.av_extension_val, (u_int *) &objp->kdbe_val_t_u.av_extension.av_extension_len, ~0)) return (FALSE); break; } return (TRUE); } bool_t xdr_kdbe_t(xdrs, objp) XDR *xdrs; kdbe_t *objp; { rpc_inline_t *buf __unused; if (!xdr_array(xdrs, (char **)&objp->kdbe_t_val, (u_int *) &objp->kdbe_t_len, ~0, sizeof (kdbe_val_t), (xdrproc_t)xdr_kdbe_val_t)) return (FALSE); return (TRUE); } bool_t xdr_kdb_incr_update_t(xdrs, objp) XDR *xdrs; kdb_incr_update_t *objp; { rpc_inline_t *buf __unused; if (!xdr_utf8str_t(xdrs, &objp->kdb_princ_name)) return (FALSE); if (!xdr_kdb_sno_t(xdrs, &objp->kdb_entry_sno)) return (FALSE); if (!xdr_kdbe_time_t(xdrs, &objp->kdb_time)) return (FALSE); if (!xdr_kdbe_t(xdrs, &objp->kdb_update)) return (FALSE); if (!xdr_bool(xdrs, &objp->kdb_deleted)) return (FALSE); if (!xdr_bool(xdrs, &objp->kdb_commit)) return (FALSE); if (!xdr_array(xdrs, (char **)&objp->kdb_kdcs_seen_by.kdb_kdcs_seen_by_val, (u_int *) &objp->kdb_kdcs_seen_by.kdb_kdcs_seen_by_len, ~0, sizeof (utf8str_t), (xdrproc_t)xdr_utf8str_t)) return (FALSE); if (!xdr_bytes(xdrs, (char **)&objp->kdb_futures.kdb_futures_val, (u_int *) &objp->kdb_futures.kdb_futures_len, ~0)) return (FALSE); return (TRUE); } bool_t xdr_kdb_ulog_t(xdrs, objp) XDR *xdrs; kdb_ulog_t *objp; { rpc_inline_t *buf __unused; if (!xdr_array(xdrs, (char **)&objp->kdb_ulog_t_val, (u_int *) &objp->kdb_ulog_t_len, ~0, sizeof (kdb_incr_update_t), (xdrproc_t)xdr_kdb_incr_update_t)) return (FALSE); return (TRUE); } bool_t xdr_update_status_t(xdrs, objp) XDR *xdrs; update_status_t *objp; { rpc_inline_t *buf __unused; if (!xdr_enum(xdrs, (enum_t *)objp)) return (FALSE); return (TRUE); } bool_t xdr_kdb_last_t(xdrs, objp) XDR *xdrs; kdb_last_t *objp; { rpc_inline_t *buf __unused; if (!xdr_kdb_sno_t(xdrs, &objp->last_sno)) return (FALSE); if (!xdr_kdbe_time_t(xdrs, &objp->last_time)) return (FALSE); return (TRUE); } bool_t xdr_kdb_incr_result_t(xdrs, objp) XDR *xdrs; kdb_incr_result_t *objp; { rpc_inline_t *buf __unused; if (!xdr_kdb_last_t(xdrs, &objp->lastentry)) return (FALSE); if (!xdr_kdb_ulog_t(xdrs, &objp->updates)) return (FALSE); if (!xdr_update_status_t(xdrs, &objp->ret)) return (FALSE); return (TRUE); } bool_t xdr_kdb_fullresync_result_t(xdrs, objp) XDR *xdrs; kdb_fullresync_result_t *objp; { rpc_inline_t *buf __unused; if (!xdr_kdb_last_t(xdrs, &objp->lastentry)) return (FALSE); if (!xdr_update_status_t(xdrs, &objp->ret)) return (FALSE); return (TRUE); } /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include /* getenv, exit */ #include #include #include #include #include #include /* rlimit */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "misc.h" extern int setup_gss_names(struct svc_req *, char **, char **); extern gss_name_t get_clnt_name(struct svc_req *); extern char *client_addr(struct svc_req *, char *); extern void *global_server_handle; extern int nofork; extern short l_port; static char abuf[33]; static char *reply_ok_str = "UPDATE_OK"; static char *reply_err_str = "UPDATE_ERROR"; static char *reply_fr_str = "UPDATE_FULL_RESYNC_NEEDED"; static char *reply_busy_str = "UPDATE_BUSY"; static char *reply_nil_str = "UPDATE_NIL"; static char *reply_perm_str = "UPDATE_PERM_DENIED"; static char *reply_unknown_str = ""; #define LOG_UNAUTH gettext("Unauthorized request: %s, %s, " \ "client=%s, service=%s, addr=%s") #define LOG_DONE gettext("Request: %s, %s, %s, client=%s, " \ "service=%s, addr=%s") #define KDB5_UTIL_DUMP_STR "/usr/sbin/kdb5_util dump -i " #ifdef DPRINT #undef DPRINT #endif #define DPRINT(i) if (nofork) printf i #ifdef POSIX_SIGNALS static struct sigaction s_action; #endif /* POSIX_SIGNALS */ static void debprret(char *w, update_status_t ret, kdb_sno_t sno) { switch (ret) { case UPDATE_OK: printf("%s: end (OK, sno=%u)\n", w, sno); break; case UPDATE_ERROR: printf("%s: end (ERROR)\n", w); break; case UPDATE_FULL_RESYNC_NEEDED: printf("%s: end (FR NEEDED)\n", w); break; case UPDATE_BUSY: printf("%s: end (BUSY)\n", w); break; case UPDATE_NIL: printf("%s: end (NIL)\n", w); break; case UPDATE_PERM_DENIED: printf("%s: end (PERM)\n", w); break; default: printf("%s: end (UNKNOWN return code (%d))\n", w, ret); } } static char * replystr(update_status_t ret) { switch (ret) { case UPDATE_OK: return (reply_ok_str); case UPDATE_ERROR: return (reply_err_str); case UPDATE_FULL_RESYNC_NEEDED: return (reply_fr_str); case UPDATE_BUSY: return (reply_busy_str); case UPDATE_NIL: return (reply_nil_str); case UPDATE_PERM_DENIED: return (reply_perm_str); default: return (reply_unknown_str); } } kdb_incr_result_t * iprop_get_updates_1(kdb_last_t *arg, struct svc_req *rqstp) { static kdb_incr_result_t ret; char *whoami = "iprop_get_updates_1"; int kret; kadm5_server_handle_t handle = global_server_handle; char *client_name = NULL, *service_name = NULL; gss_name_t name = NULL; OM_uint32 min_stat; char obuf[256] = {0}; /* default return code */ ret.ret = UPDATE_ERROR; DPRINT(("%s: start, last_sno=%u\n", whoami, (ulong_t)arg->last_sno)); if (!handle) { krb5_klog_syslog(LOG_ERR, gettext("%s: server handle is NULL"), whoami); goto out; } if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { krb5_klog_syslog(LOG_ERR, gettext("%s: setup_gss_names failed"), whoami); goto out; } DPRINT(("%s: clprinc=`%s'\n\tsvcprinc=`%s'\n", whoami, client_name, service_name)); if (!(name = get_clnt_name(rqstp))) { krb5_klog_syslog(LOG_ERR, gettext("%s: Couldn't obtain client's name"), whoami); goto out; } if (!kadm5int_acl_check(handle->context, name, ACL_IPROP, NULL, NULL)) { ret.ret = UPDATE_PERM_DENIED; audit_kadmind_unauth(rqstp->rq_xprt, l_port, whoami, "", client_name); krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, whoami, "", client_name, service_name, client_addr(rqstp, abuf)); goto out; } kret = ulog_get_entries(handle->context, *arg, &ret); if (ret.ret == UPDATE_OK) { (void) snprintf(obuf, sizeof (obuf), gettext("%s; Incoming SerialNo=%u; Outgoing SerialNo=%u"), replystr(ret.ret), (ulong_t)arg->last_sno, (ulong_t)ret.lastentry.last_sno); } else { (void) snprintf(obuf, sizeof (obuf), gettext("%s; Incoming SerialNo=%u; Outgoing SerialNo=N/A"), replystr(ret.ret), (ulong_t)arg->last_sno); } audit_kadmind_auth(rqstp->rq_xprt, l_port, whoami, obuf, client_name, kret); krb5_klog_syslog(LOG_NOTICE, LOG_DONE, whoami, obuf, ((kret == 0) ? "success" : error_message(kret)), client_name, service_name, client_addr(rqstp, abuf)); out: if (nofork) debprret(whoami, ret.ret, ret.lastentry.last_sno); if (client_name) free(client_name); if (service_name) free(service_name); if (name) gss_release_name(&min_stat, &name); return (&ret); } /* * Given a client princ (foo/fqdn@R), copy (in arg cl) the fqdn substring. * Return arg cl str ptr on success, else NULL. */ static char * getclhoststr(char *clprinc, char *cl, int len) { char *s; if (s = strchr(clprinc, '/')) { if (!++s || strlcpy(cl, s, len) >= len) { return (NULL); } if (s = strchr(cl, '@')) { *s = '\0'; return (cl); /* success */ } } return (NULL); } kdb_fullresync_result_t * iprop_full_resync_1( /* LINTED */ void *argp, struct svc_req *rqstp) { static kdb_fullresync_result_t ret; char tmpf[MAX_FILENAME] = {0}; char ubuf[MAX_FILENAME + sizeof (KDB5_UTIL_DUMP_STR)] = {0}; char clhost[MAXHOSTNAMELEN] = {0}; int pret, fret; kadm5_server_handle_t handle = global_server_handle; OM_uint32 min_stat; gss_name_t name = NULL; char *client_name = NULL, *service_name = NULL; char *whoami = "iprop_full_resync_1"; /* default return code */ ret.ret = UPDATE_ERROR; if (!handle) { krb5_klog_syslog(LOG_ERR, gettext("%s: server handle is NULL"), whoami); goto out; } DPRINT(("%s: start\n", whoami)); if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { krb5_klog_syslog(LOG_ERR, gettext("%s: setup_gss_names failed"), whoami); goto out; } DPRINT(("%s: clprinc=`%s'\n\tsvcprinc=`%s'\n", whoami, client_name, service_name)); if (!(name = get_clnt_name(rqstp))) { krb5_klog_syslog(LOG_ERR, gettext("%s: Couldn't obtain client's name"), whoami); goto out; } if (!kadm5int_acl_check(handle->context, name, ACL_IPROP, NULL, NULL)) { ret.ret = UPDATE_PERM_DENIED; audit_kadmind_unauth(rqstp->rq_xprt, l_port, whoami, "", client_name); krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, whoami, "", client_name, service_name, client_addr(rqstp, abuf)); goto out; } if (!getclhoststr(client_name, clhost, sizeof (clhost))) { krb5_klog_syslog(LOG_ERR, gettext("%s: getclhoststr failed"), whoami); goto out; } /* * construct db dump file name; kprop style name + clnt fqdn */ (void) strcpy(tmpf, "/var/krb5/slave_datatrans_"); if (strlcat(tmpf, clhost, sizeof (tmpf)) >= sizeof (tmpf)) { krb5_klog_syslog(LOG_ERR, gettext("%s: db dump file name too long; max length=%d"), whoami, (sizeof (tmpf) - 1)); goto out; } /* * note the -i; modified version of kdb5_util dump format * to include sno (serial number) */ if (strlcpy(ubuf, KDB5_UTIL_DUMP_STR, sizeof (ubuf)) >= sizeof (ubuf)) { goto out; } if (strlcat(ubuf, tmpf, sizeof (ubuf)) >= sizeof (ubuf)) { krb5_klog_syslog(LOG_ERR, gettext("%s: kdb5 util dump string too long; max length=%d"), whoami, (sizeof (ubuf) - 1)); goto out; } /* * Fork to dump the db and xfer it to the slave. * (the fork allows parent to return quickly and the child * acts like a callback to the slave). */ fret = fork(); DPRINT(("%s: fork=%d (%d)\n", whoami, fret, getpid())); switch (fret) { case -1: /* error */ if (nofork) { perror(whoami); } krb5_klog_syslog(LOG_ERR, gettext("%s: fork failed: %s"), whoami, error_message(errno)); goto out; case 0: /* child */ DPRINT(("%s: run `%s' ...\n", whoami, ubuf)); #ifdef POSIX_SIGNALS (void) sigemptyset(&s_action.sa_mask); s_action.sa_handler = SIG_DFL; (void) sigaction(SIGCHLD, &s_action, (struct sigaction *) NULL); #else (void) signal(SIGCHLD, SIG_DFL); #endif /* POSIX_SIGNALS */ /* run kdb5_util(8) dump for IProp */ pret = pclose(popen(ubuf, "w")); DPRINT(("%s: pclose=%d\n", whoami, pret)); if (pret == -1) { if (nofork) { perror(whoami); } krb5_klog_syslog(LOG_ERR, gettext("%s: pclose(popen) failed: %s"), whoami, error_message(errno)); goto out; } DPRINT(("%s: exec `kprop -f %s %s' ...\n", whoami, tmpf, clhost)); pret = execl("/usr/lib/krb5/kprop", "kprop", "-f", tmpf, clhost, NULL); if (pret == -1) { if (nofork) { perror(whoami); } krb5_klog_syslog(LOG_ERR, gettext("%s: exec failed: %s"), whoami, error_message(errno)); goto out; } /* NOTREACHED */ break; default: /* parent */ ret.ret = UPDATE_OK; /* not used by slave (sno is retrieved from kdb5_util dump) */ ret.lastentry.last_sno = 0; ret.lastentry.last_time.seconds = 0; ret.lastentry.last_time.useconds = 0; audit_kadmind_auth(rqstp->rq_xprt, l_port, whoami, "", client_name, 0); krb5_klog_syslog(LOG_NOTICE, LOG_DONE, whoami, "", "success", client_name, service_name, client_addr(rqstp, abuf)); goto out; } out: if (nofork) debprret(whoami, ret.ret, 0); if (client_name) free(client_name); if (service_name) free(service_name); if (name) gss_release_name(&min_stat, &name); return (&ret); } void krb5_iprop_prog_1( struct svc_req *rqstp, register SVCXPRT *transp) { union { kdb_last_t iprop_get_updates_1_arg; } argument; char *result; bool_t (*_xdr_argument)(), (*_xdr_result)(); char *(*local)(); char *whoami = "krb5_iprop_prog_1"; switch (rqstp->rq_proc) { case NULLPROC: (void) svc_sendreply(transp, xdr_void, (char *)NULL); return; case IPROP_GET_UPDATES: _xdr_argument = xdr_kdb_last_t; _xdr_result = xdr_kdb_incr_result_t; local = (char *(*)()) iprop_get_updates_1; break; case IPROP_FULL_RESYNC: _xdr_argument = xdr_void; _xdr_result = xdr_kdb_fullresync_result_t; local = (char *(*)()) iprop_full_resync_1; break; default: krb5_klog_syslog(LOG_ERR, gettext("RPC unknown request: %d (%s)"), rqstp->rq_proc, whoami); svcerr_noproc(transp); return; } (void) memset((char *)&argument, 0, sizeof (argument)); if (!svc_getargs(transp, _xdr_argument, (caddr_t)&argument)) { krb5_klog_syslog(LOG_ERR, gettext("RPC svc_getargs failed (%s)"), whoami); svcerr_decode(transp); return; } result = (*local)(&argument, rqstp); if (_xdr_result && result != NULL && !svc_sendreply(transp, _xdr_result, result)) { krb5_klog_syslog(LOG_ERR, gettext("RPC svc_sendreply failed (%s)"), whoami); svcerr_systemerr(transp); } if (!svc_freeargs(transp, _xdr_argument, (caddr_t)&argument)) { krb5_klog_syslog(LOG_ERR, gettext("RPC svc_freeargs failed (%s)"), whoami); exit(1); } if (rqstp->rq_proc == IPROP_GET_UPDATES) { /* LINTED */ kdb_incr_result_t *r = (kdb_incr_result_t *)result; if (r->ret == UPDATE_OK) { ulog_free_entries(r->updates.kdb_ulog_t_val, r->updates.kdb_ulog_t_len); r->updates.kdb_ulog_t_val = NULL; r->updates.kdb_ulog_t_len = 0; } } } /* * Get the host base service name for the kiprop principal. Returns * KADM5_OK on success. Caller must free the storage allocated for * host_service_name. */ kadm5_ret_t kiprop_get_adm_host_srv_name( krb5_context context, const char *realm, char **host_service_name) { kadm5_ret_t ret; char *name; char *host; if (ret = kadm5_get_master(context, realm, &host)) return (ret); name = malloc(strlen(KIPROP_SVC_NAME)+ strlen(host) + 2); if (name == NULL) { free(host); return (ENOMEM); } (void) sprintf(name, "%s@%s", KIPROP_SVC_NAME, host); free(host); *host_service_name = name; return (KADM5_OK); } /* * Copyright 2008 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 * */ /* * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. * */ #include #include #include /* SUNWresync 121 XXX */ #include /* for gss_nt_krb5_name */ #include #ifdef HAVE_MEMORY_H #include #endif #include #include #include #include #include #ifdef HAVE_ARPA_INET_H #include #endif #include "misc.h" #include "kadm5/server_internal.h" extern void *global_server_handle; void log_badauth(OM_uint32 major, OM_uint32 minor, struct sockaddr_in *addr, char *data); /* * Function: kadm_1 * * Purpose: RPC proccessing procedure. * originally generated from rpcgen * * Arguments: * rqstp (input) rpc request structure * transp (input) rpc transport structure * (input/output) * * * Requires: * Effects: * Modifies: */ void kadm_1(rqstp, transp) struct svc_req *rqstp; register SVCXPRT *transp; { union { cprinc_arg create_principal_2_arg; dprinc_arg delete_principal_2_arg; mprinc_arg modify_principal_2_arg; rprinc_arg rename_principal_2_arg; gprinc_arg get_principal_2_arg; chpass_arg chpass_principal_2_arg; chrand_arg chrand_principal_2_arg; cpol_arg create_policy_2_arg; dpol_arg delete_policy_2_arg; mpol_arg modify_policy_2_arg; gpol_arg get_policy_2_arg; setkey_arg setkey_principal_2_arg; setv4key_arg setv4key_principal_2_arg; cprinc3_arg create_principal3_2_arg; chpass3_arg chpass_principal3_2_arg; chrand3_arg chrand_principal3_2_arg; setkey3_arg setkey_principal3_2_arg; } argument; char *result; bool_t (*xdr_argument)(), (*xdr_result)(); char *(*local)(); if (rqstp->rq_cred.oa_flavor != RPCSEC_GSS) { krb5_klog_syslog(LOG_ERR, gettext("Authentication attempt failed: invalid " "RPC authentication flavor %d"), rqstp->rq_cred.oa_flavor); svcerr_weakauth(transp); return; } switch (rqstp->rq_proc) { case NULLPROC: (void) svc_sendreply(transp, xdr_void, (char *)NULL); return; case CREATE_PRINCIPAL: xdr_argument = xdr_cprinc_arg; xdr_result = xdr_generic_ret; local = (char *(*)()) create_principal_2_svc; break; case DELETE_PRINCIPAL: xdr_argument = xdr_dprinc_arg; xdr_result = xdr_generic_ret; local = (char *(*)()) delete_principal_2_svc; break; case MODIFY_PRINCIPAL: xdr_argument = xdr_mprinc_arg; xdr_result = xdr_generic_ret; local = (char *(*)()) modify_principal_2_svc; break; case RENAME_PRINCIPAL: xdr_argument = xdr_rprinc_arg; xdr_result = xdr_generic_ret; local = (char *(*)()) rename_principal_2_svc; break; case GET_PRINCIPAL: xdr_argument = xdr_gprinc_arg; xdr_result = xdr_gprinc_ret; local = (char *(*)()) get_principal_2_svc; break; case GET_PRINCS: xdr_argument = xdr_gprincs_arg; xdr_result = xdr_gprincs_ret; local = (char *(*)()) get_princs_2_svc; break; case CHPASS_PRINCIPAL: xdr_argument = xdr_chpass_arg; xdr_result = xdr_generic_ret; local = (char *(*)()) chpass_principal_2_svc; break; #ifdef SUNWOFF case SETV4KEY_PRINCIPAL: xdr_argument = xdr_setv4key_arg; xdr_result = xdr_generic_ret; local = (char *(*)()) setv4key_principal_2_svc; break; #endif case SETKEY_PRINCIPAL: xdr_argument = xdr_setkey_arg; xdr_result = xdr_generic_ret; local = (char *(*)()) setkey_principal_2_svc; break; case CHRAND_PRINCIPAL: xdr_argument = xdr_chrand_arg; xdr_result = xdr_chrand_ret; local = (char *(*)()) chrand_principal_2_svc; break; case CREATE_POLICY: xdr_argument = xdr_cpol_arg; xdr_result = xdr_generic_ret; local = (char *(*)()) create_policy_2_svc; break; case DELETE_POLICY: xdr_argument = xdr_dpol_arg; xdr_result = xdr_generic_ret; local = (char *(*)()) delete_policy_2_svc; break; case MODIFY_POLICY: xdr_argument = xdr_mpol_arg; xdr_result = xdr_generic_ret; local = (char *(*)()) modify_policy_2_svc; break; case GET_POLICY: xdr_argument = xdr_gpol_arg; xdr_result = xdr_gpol_ret; local = (char *(*)()) get_policy_2_svc; break; case GET_POLS: xdr_argument = xdr_gpols_arg; xdr_result = xdr_gpols_ret; local = (char *(*)()) get_pols_2_svc; break; case GET_PRIVS: xdr_argument = xdr_u_int; xdr_result = xdr_getprivs_ret; local = (char *(*)()) get_privs_2_svc; break; case INIT: xdr_argument = xdr_u_int; xdr_result = xdr_generic_ret; local = (char *(*)()) init_2_svc; break; case CREATE_PRINCIPAL3: xdr_argument = xdr_cprinc3_arg; xdr_result = xdr_generic_ret; local = (char *(*)()) create_principal3_2_svc; break; case CHPASS_PRINCIPAL3: xdr_argument = xdr_chpass3_arg; xdr_result = xdr_generic_ret; local = (char *(*)()) chpass_principal3_2_svc; break; case CHRAND_PRINCIPAL3: xdr_argument = xdr_chrand3_arg; xdr_result = xdr_chrand_ret; local = (char *(*)()) chrand_principal3_2_svc; break; case SETKEY_PRINCIPAL3: xdr_argument = xdr_setkey3_arg; xdr_result = xdr_generic_ret; local = (char *(*)()) setkey_principal3_2_svc; break; default: krb5_klog_syslog(LOG_ERR, gettext("Invalid KADM5 procedure number: %d"), rqstp->rq_proc); svcerr_noproc(transp); return; } memset((char *)&argument, 0, sizeof(argument)); if (!svc_getargs(transp, xdr_argument, (char *) &argument)) { svcerr_decode(transp); return; } result = (*local)(&argument, rqstp); if (result != NULL && !svc_sendreply(transp, xdr_result, (char *) result)) { krb5_klog_syslog(LOG_ERR, gettext("WARNING! Unable to send function results, " "continuing.")); svcerr_systemerr(transp); } if (!svc_freeargs(transp, xdr_argument, (char *) &argument)) { krb5_klog_syslog(LOG_ERR, gettext("WARNING! Unable to free arguments, " "continuing.")); } return; } /* * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. */ /* * 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 * */ /* * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved * */ #include #include #include #include #include "misc.h" /* * Function: chpass_principal_wrapper_3 * * Purpose: wrapper to kadm5_chpass_principal that checks to see if * pw_min_life has been reached. if not it returns an error. * otherwise it calls kadm5_chpass_principal * * Arguments: * principal (input) krb5_principals whose password we are * changing * keepold (input) whether to preserve old keys * n_ks_tuple (input) the number of key-salt tuples in ks_tuple * ks_tuple (input) array of tuples indicating the caller's * requested enctypes/salttypes * password (input) password we are going to change to. * 0 on success error code on failure. * * Requires: * kadm5_init to have been run. * * Effects: * calls kadm5_chpass_principal which changes the kdb and the * the admin db. * */ kadm5_ret_t chpass_principal_wrapper_3(void *server_handle, krb5_principal principal, krb5_boolean keepold, int n_ks_tuple, krb5_key_salt_tuple *ks_tuple, char *password) { kadm5_ret_t ret; /* Solaris Kerberos */ ret = kadm5_check_min_life(server_handle, principal, NULL, 0); if (ret) return ret; return kadm5_chpass_principal_3(server_handle, principal, keepold, n_ks_tuple, ks_tuple, password); } /* * Function: randkey_principal_wrapper_3 * * Purpose: wrapper to kadm5_randkey_principal which checks the * password's min. life. * * Arguments: * principal (input) krb5_principal whose password we are * changing * keepold (input) whether to preserve old keys * n_ks_tuple (input) the number of key-salt tuples in ks_tuple * ks_tuple (input) array of tuples indicating the caller's * requested enctypes/salttypes * key (output) new random key * 0, error code on error. * * Requires: * kadm5_init needs to be run * * Effects: * calls kadm5_randkey_principal * */ kadm5_ret_t randkey_principal_wrapper_3(void *server_handle, krb5_principal principal, krb5_boolean keepold, int n_ks_tuple, krb5_key_salt_tuple *ks_tuple, krb5_keyblock **keys, int *n_keys) { kadm5_ret_t ret; /* Solaris Kerberos */ ret = kadm5_check_min_life(server_handle, principal, NULL, 0); if (ret) return ret; return kadm5_randkey_principal_3(server_handle, principal, keepold, n_ks_tuple, ks_tuple, keys, n_keys); } kadm5_ret_t schpw_util_wrapper(void *server_handle, krb5_principal princ, char *new_pw, char **ret_pw, char *msg_ret, unsigned int msg_len) { kadm5_ret_t ret; /* Solaris Kerberos */ ret = kadm5_check_min_life(server_handle, princ, msg_ret, msg_len); if (ret) return ret; return kadm5_chpass_principal_util(server_handle, princ, new_pw, ret_pw, msg_ret, msg_len); } kadm5_ret_t randkey_principal_wrapper(void *server_handle, krb5_principal princ, krb5_keyblock ** keys, int *n_keys) { kadm5_ret_t ret; /* Solaris Kerberos */ ret = kadm5_check_min_life(server_handle, princ, NULL, 0); if (ret) return ret; return kadm5_randkey_principal(server_handle, princ, keys, n_keys); } /* * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. */ #ifndef _MISC_H #define _MISC_H #ifdef __cplusplus extern "C" { #endif /* * 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 * */ /* * Copyright 1994 OpenVision Technologies, Inc., All Rights Reserved * */ kadm5_ret_t chpass_principal_wrapper_3(void *server_handle, krb5_principal principal, krb5_boolean keepold, int n_ks_tuple, krb5_key_salt_tuple *ks_tuple, char *password); kadm5_ret_t randkey_principal_wrapper_3(void *server_handle, krb5_principal principal, krb5_boolean keepold, int n_ks_tuple, krb5_key_salt_tuple *ks_tuple, krb5_keyblock **keys, int *n_keys); kadm5_ret_t schpw_util_wrapper(void *server_handle, krb5_principal princ, char *new_pw, char **ret_pw, char *msg_ret, unsigned int msg_len); kadm5_ret_t kadm5_get_principal_v1(void *server_handle, krb5_principal principal, kadm5_principal_ent_t_v1 *ent); kadm5_ret_t kadm5_get_policy_v1(void *server_handle, kadm5_policy_t name, kadm5_policy_ent_t *ent); krb5_error_code process_chpw_request(krb5_context context, void *server_handle, char *realm, int s, krb5_keytab keytab, struct sockaddr_in *sockin, krb5_data *req, krb5_data *rep); #ifdef SVC_GETARGS void kadm_1(struct svc_req *, SVCXPRT *); #endif void trunc_name(size_t *len, char **dots); #ifdef __cplusplus } #endif #endif /* !_MISC_H */ /* * Copyright 2009 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 * */ /* * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved * */ /* * Copyright (C) 1998 by the FundsXpress, INC. * * 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 FundsXpress. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. FundsXpress makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ /* * SUNWresync121 XXX * Beware future resyncers, this file is much diff from MIT (1.0...) */ #include #include #include #include #include #ifdef _AIX #include #endif #include #include #include #include #include /* inet_ntoa */ #include #include "gssapiP_krb5.h" /* for kg_get_context */ #include #include #include #include #include #include "kdb_kt.h" /* for krb5_ktkdb_set_context */ #include #include #include #include #include #include #include #include #include #include "misc.h" #ifdef PURIFY #include "purify.h" int signal_pure_report = 0; int signal_pure_clear = 0; void request_pure_report(int); void request_pure_clear(int); #endif /* PURIFY */ #ifndef FD_SETSIZE #define FD_SETSIZE 256 #endif #ifndef MAX #define MAX(a, b) (((a) > (b)) ? (a) : (b)) #endif #if defined(NEED_DAEMON_PROTO) extern int daemon(int, int); #endif static int signal_request_exit = 0; kadm5_config_params chgpw_params; void setup_signal_handlers(iprop_role iproprole); void request_exit(int); void sig_pipe(int); void kadm_svc_run(void); #ifdef POSIX_SIGNALS static struct sigaction s_action; #endif /* POSIX_SIGNALS */ #define TIMEOUT 15 typedef struct _auth_gssapi_name { char *name; gss_OID type; } auth_gssapi_name; gss_name_t gss_changepw_name = NULL, gss_oldchangepw_name = NULL; void *global_server_handle; /* * This is a kludge, but the server needs these constants to be * compatible with old clients. They are defined in , * but only if USE_KADM5_API_VERSION == 1. */ #define OVSEC_KADM_ADMIN_SERVICE_P "ovsec_adm@admin" #define OVSEC_KADM_CHANGEPW_SERVICE_P "ovsec_adm@changepw" extern void krb5_iprop_prog_1(); extern kadm5_ret_t kiprop_get_adm_host_srv_name( krb5_context, const char *, char **); static int schpw; in_port_t l_port = 0; /* global local port num, for BSM audits */ int nofork = 0; /* global; don't fork (debug mode) */ /* * Function: usage * * Purpose: print out the server usage message * * Arguments: * Requires: * Effects: * Modifies: */ static void usage() { fprintf(stderr, gettext("Usage: kadmind [-x db_args]* [-r realm] [-m] [-d] " "[-p port-number]\n")); exit(1); } /* * Function: display_status * * Purpose: displays GSS-API messages * * Arguments: * * msg a string to be displayed with the message * maj_stat the GSS-API major status code * min_stat the GSS-API minor status code * * Effects: * * The GSS-API messages associated with maj_stat and min_stat are * displayed on stderr, each preceeded by "GSS-API error : " and * followed by a newline. */ static void display_status_1(char *, OM_uint32, int); static void display_status(msg, maj_stat, min_stat) char *msg; OM_uint32 maj_stat; OM_uint32 min_stat; { display_status_1(msg, maj_stat, GSS_C_GSS_CODE); display_status_1(msg, min_stat, GSS_C_MECH_CODE); } static void display_status_1(m, code, type) char *m; OM_uint32 code; int type; { OM_uint32 min_stat; gss_buffer_desc msg; OM_uint32 msg_ctx; msg_ctx = 0; while (1) { (void) gss_display_status(&min_stat, code, type, GSS_C_NULL_OID, &msg_ctx, &msg); fprintf(stderr, "GSS-API error %s: %s\n", m, (char *)msg.value); (void) gss_release_buffer(&min_stat, &msg); if (!msg_ctx) break; } } /* * Solaris Kerberos: the following prototypes are needed because these are * private interfaces that do not have prototypes in any .h */ extern struct hostent *res_getipnodebyaddr(const void *, size_t, int, int *); extern void res_freehostent(struct hostent *); static void freedomnames(char **npp) { char **tpp; if (npp) { tpp = npp; while (*tpp++) { free(*(tpp-1)); } free(npp); } } /* * Construct a list of uniq FQDNs of all the net interfaces (except * krb5.conf master dups) and return it in arg 'dnames'. * * On successful return (0), caller must call freedomnames() * to free memory. */ static int getdomnames(krb5_context ctx, char *realm, char ***dnames) { krb5_address **addresses = NULL; krb5_address *a = NULL; struct hostent *hp = NULL; int ret, i, result=0, error; char **npp = NULL, **tpp=NULL; int dup=0, n = 0; char *cfhost = NULL; /* krb5 conf file master hostname */ if (ret = kadm5_get_master(ctx, realm, &cfhost)) { return (ret); } ret = krb5_os_localaddr(ctx, &addresses); if (ret != 0) { if (nofork) (void) fprintf(stderr, "kadmind: get localaddrs failed: %s", error_message(ret)); result = ret; goto err; } for (i=0; addresses[i]; i++) { a = addresses[i]; hp = res_getipnodebyaddr(a->contents, a->length, a->addrtype == ADDRTYPE_INET ? AF_INET : AF_INET6, &error); if (hp != NULL) { /* skip master host in krb5.conf */ if (strcasecmp(cfhost, hp->h_name) == 0) { res_freehostent(hp); hp = NULL; continue; } dup = 0; tpp = npp; /* skip if hostname already exists in list */ while (tpp && *tpp++) { if (strcasecmp(*(tpp-1), hp->h_name) == 0) { dup++; break; } } if (dup) { res_freehostent(hp); hp = NULL; continue; } npp = realloc(npp, sizeof(char *) * (n + 2)); if (!npp) { result = ENOMEM; goto err; } npp[n] = strdup(hp->h_name); if (!npp[n]) { result = ENOMEM; goto err; } npp[n+1] = NULL; n++; res_freehostent(hp); hp = NULL; result = 0; } } #ifdef DEBUG printf("getdomnames: n=%d, i=%d, npp=%p\n", n, i, npp); tpp = npp; while (tpp && *tpp++) { printf("tpp=%s\n", *(tpp-1)); } #endif goto out; err: if (npp) { freedomnames(npp); npp = NULL; } if (hp) { res_freehostent(hp); hp = NULL; } out: if (cfhost) { free (cfhost); cfhost = NULL; } if (addresses) { krb5_free_addresses(ctx, addresses); addresses = NULL; } if (result == 0) *dnames = npp; return (result); } /* * Set the rpcsec_gss svc names for all net interfaces. */ static void set_svc_domnames(char *svcname, char **dnames, u_int program, u_int version) { bool_t ret; char **tpp = dnames; if (!tpp) return; while (*tpp++) { /* MAX_NAME_LEN from rpc/rpcsec_gss.h */ char name[MAXHOSTNAMELEN+MAX_NAME_LEN+2] = {0}; (void) snprintf(name, sizeof(name), "%s@%s", svcname, *(tpp-1)); ret = rpc_gss_set_svc_name(name, "kerberos_v5", 0, program, version); if (nofork && ret) (void) fprintf(stderr, "rpc_gss_set_svc_name success: %s\n", name); } } /* XXX yuck. the signal handlers need this */ static krb5_context context; static krb5_context hctx; int main(int argc, char *argv[]) { register SVCXPRT *transp; extern char *optarg; extern int optind, opterr; int ret, rlen, oldnames = 0; OM_uint32 OMret, major_status, minor_status; char *whoami; FILE *acl_file; gss_buffer_desc in_buf; struct servent *srv; struct sockaddr_in addr; struct sockaddr_in *sin; int s; auth_gssapi_name names[6]; gss_buffer_desc gssbuf; gss_OID nt_krb5_name_oid; int optchar; struct netconfig *nconf; void *handlep; int fd; struct t_info tinfo; struct t_bind tbindstr, *tres; struct t_optmgmt req, resp; struct opthdr *opt; char reqbuf[128]; struct rlimit rl; char *kiprop_name = NULL; /* IProp svc name */ kdb_log_context *log_ctx; kadm5_server_handle_t handle; krb5_context ctx; kadm5_config_params params; char **db_args = NULL; int db_args_size = 0; const char *errmsg; char **dnames = NULL; int retdn; int iprop_supported; /* Solaris Kerberos: Stores additional error messages */ char *emsg = NULL; /* Solaris Kerberos: Indicates whether loalhost is master or not */ krb5_boolean is_master; /* Solaris Kerberos: Used for checking acl file */ gss_name_t name; /* This is OID value the Krb5_Name NameType */ gssbuf.value = "{1 2 840 113554 1 2 2 1}"; gssbuf.length = strlen(gssbuf.value); major_status = gss_str_to_oid(&minor_status, &gssbuf, &nt_krb5_name_oid); if (major_status != GSS_S_COMPLETE) { fprintf(stderr, gettext("Couldn't create KRB5 Name NameType OID\n")); display_status("str_to_oid", major_status, minor_status); exit(1); } names[0].name = names[1].name = names[2].name = names[3].name = NULL; names[4].name = names[5].name =NULL; names[0].type = names[1].type = names[2].type = names[3].type = (gss_OID) nt_krb5_name_oid; names[4].type = names[5].type = (gss_OID) nt_krb5_name_oid; #ifdef PURIFY purify_start_batch(); #endif /* PURIFY */ whoami = (strrchr(argv[0], '/') ? strrchr(argv[0], '/')+1 : argv[0]); (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); nofork = 0; memset((char *) ¶ms, 0, sizeof(params)); while ((optchar = getopt(argc, argv, "r:mdp:x:")) != EOF) { switch (optchar) { case 'r': if (!optarg) usage(); params.realm = optarg; params.mask |= KADM5_CONFIG_REALM; break; case 'm': params.mkey_from_kbd = 1; params.mask |= KADM5_CONFIG_MKEY_FROM_KBD; break; case 'd': nofork = 1; break; case 'p': if (!optarg) usage(); params.kadmind_port = atoi(optarg); params.mask |= KADM5_CONFIG_KADMIND_PORT; break; case 'x': if (!optarg) usage(); db_args_size++; { char **temp = realloc( db_args, sizeof(char*) * (db_args_size+1)); /* one for NULL */ if( temp == NULL ) { fprintf(stderr, gettext("%s: cannot initialize. Not enough memory\n"), whoami); exit(1); } db_args = temp; } db_args[db_args_size-1] = optarg; db_args[db_args_size] = NULL; break; case '?': default: usage(); } } if (getrlimit(RLIMIT_NOFILE, &rl) == 0) { rl.rlim_cur = rl.rlim_max = MAX(rl.rlim_max, FD_SETSIZE); (void) setrlimit(RLIMIT_NOFILE, &rl); (void) enable_extended_FILE_stdio(-1, -1); } if ((ret = kadm5_init_krb5_context(&context))) { fprintf(stderr, gettext("%s: %s while initializing context, aborting\n"), whoami, error_message(ret)); exit(1); } krb5_klog_init(context, "admin_server", whoami, 1); /* Solaris Kerberos */ if((ret = kadm5_init2("kadmind", NULL, NULL, ¶ms, KADM5_STRUCT_VERSION, KADM5_API_VERSION_2, db_args, &global_server_handle, &emsg)) != KADM5_OK) { krb5_klog_syslog(LOG_ERR, gettext("%s while initializing, aborting"), (emsg ? emsg : error_message(ret))); fprintf(stderr, gettext("%s: %s while initializing, aborting\n"), whoami, (emsg ? emsg : error_message(ret))); if (emsg) free(emsg); krb5_klog_close(context); exit(1); } if( db_args ) { free(db_args), db_args=NULL; } if ((ret = kadm5_get_config_params(context, 1, ¶ms, ¶ms))) { const char *e_txt = krb5_get_error_message (context, ret); /* Solaris Kerberos: Remove double "whoami" */ krb5_klog_syslog(LOG_ERR, gettext("%s while initializing, aborting"), e_txt); fprintf(stderr, gettext("%s: %s while initializing, aborting\n"), whoami, e_txt); kadm5_destroy(global_server_handle); krb5_klog_close(context); exit(1); } #define REQUIRED_PARAMS (KADM5_CONFIG_REALM | KADM5_CONFIG_ACL_FILE) if ((params.mask & REQUIRED_PARAMS) != REQUIRED_PARAMS) { /* Solaris Kerberos: Keep error messages consistent */ krb5_klog_syslog(LOG_ERR, gettext("Missing required configuration values (%lx)" "while initializing, aborting"), (params.mask & REQUIRED_PARAMS) ^ REQUIRED_PARAMS); fprintf(stderr, gettext("%s: Missing required configuration values " "(%lx) while initializing, aborting\n"), whoami, (params.mask & REQUIRED_PARAMS) ^ REQUIRED_PARAMS); krb5_klog_close(context); kadm5_destroy(global_server_handle); exit(1); } /* * When using the Horowitz/IETF protocol for * password changing, the default port is 464 * (officially recognized by IANA) * * DEFAULT_KPASSWD_PORT -> 464 */ chgpw_params.kpasswd_port = DEFAULT_KPASSWD_PORT; chgpw_params.mask |= KADM5_CONFIG_KPASSWD_PORT; chgpw_params.kpasswd_protocol = KRB5_CHGPWD_CHANGEPW_V2; chgpw_params.mask |= KADM5_CONFIG_KPASSWD_PROTOCOL; if (ret = kadm5_get_config_params(context, 1, &chgpw_params, &chgpw_params)) { /* Solaris Kerberos: Remove double "whoami" */ krb5_klog_syslog(LOG_ERR, gettext("%s while initializing," " aborting"), error_message(ret)); fprintf(stderr, gettext("%s: %s while initializing, aborting\n"), whoami, error_message(ret)); krb5_klog_close(context); exit(1); } /* * We now setup the socket and bind() to port 464, so that * kadmind can now listen to and process change-pwd requests * from non-Solaris Kerberos V5 clients such as Microsoft, * MIT, AIX, HP etc */ if ((schpw = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { const char *e_txt = krb5_get_error_message (context, ret); krb5_klog_syslog(LOG_ERR, gettext( "Cannot create simple " "chpw socket: %s"), e_txt); fprintf(stderr, gettext("Cannot create simple chpw socket: %s"), e_txt); kadm5_destroy(global_server_handle); krb5_klog_close(context); exit(1); } /* Solaris Kerberos: Ensure that kadmind is only run on a master kdc */ if (ret = kadm5_is_master(context, params.realm, &is_master)){ krb5_klog_syslog(LOG_ERR, gettext("Failed to determine whether host is master " "KDC for realm %s: %s"), params.realm, error_message(ret)); fprintf(stderr, gettext("%s: Failed to determine whether host is master " "KDC for realm %s: %s\n"), whoami, params.realm, error_message(ret)); krb5_klog_close(context); exit(1); } if (is_master == FALSE) { char *master = NULL; kadm5_get_master(context, params.realm, &master); krb5_klog_syslog(LOG_ERR, gettext("%s can only be run on the master KDC, %s, for " "realm %s"), whoami, master ? master : "unknown", params.realm); fprintf(stderr, gettext("%s: %s can only be run on the master KDC, %s, for " "realm %s\n"), whoami, whoami, master ? master: "unknown", params.realm); krb5_klog_close(context); exit(1); } memset((char *) &addr, 0, sizeof (struct sockaddr_in)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; l_port = addr.sin_port = htons(params.kadmind_port); sin = &addr; if ((handlep = setnetconfig()) == (void *) NULL) { (void) krb5_klog_syslog(LOG_ERR, gettext("cannot get any transport information")); krb5_klog_close(context); exit(1); } while (nconf = getnetconfig(handlep)) { if ((nconf->nc_semantics == NC_TPI_COTS_ORD) && (strcmp(nconf->nc_protofmly, NC_INET) == 0) && (strcmp(nconf->nc_proto, NC_TCP) == 0)) break; } if (nconf == (struct netconfig *) NULL) { (void) endnetconfig(handlep); krb5_klog_close(context); exit(1); } fd = t_open(nconf->nc_device, O_RDWR, &tinfo); if (fd == -1) { krb5_klog_syslog(LOG_ERR, gettext("unable to open connection for ADMIN server")); krb5_klog_close(context); exit(1); } /* LINTED */ opt = (struct opthdr *) reqbuf; opt->level = SOL_SOCKET; opt->name = SO_REUSEADDR; opt->len = sizeof (int); /* * The option value is "1". This will allow the server to restart * whilst the previous process is cleaning up after itself in a * FIN_WAIT_2 or TIME_WAIT state. If another process is started * outside of smf(7) then bind will fail anyway, which is what we want. */ reqbuf[sizeof (struct opthdr)] = 1; req.flags = T_NEGOTIATE; req.opt.len = sizeof (struct opthdr) + opt->len; req.opt.buf = (char *) opt; resp.flags = 0; resp.opt.buf = reqbuf; resp.opt.maxlen = sizeof (reqbuf); if (t_optmgmt(fd, &req, &resp) < 0 || resp.flags != T_SUCCESS) { t_error("t_optmgmt"); exit(1); } /* Transform addr to netbuf */ tres = (struct t_bind *) t_alloc(fd, T_BIND, T_ADDR); if (tres == NULL) { (void) t_close(fd); (void) krb5_klog_syslog(LOG_ERR, gettext("cannot allocate netbuf")); krb5_klog_close(context); exit(1); } tbindstr.qlen = 8; tbindstr.addr.buf = (char *) sin; tbindstr.addr.len = tbindstr.addr.maxlen = __rpc_get_a_size(tinfo.addr); sin = (struct sockaddr_in *) tbindstr.addr.buf; /* SUNWresync121 XXX (void) memset(&addr, 0, sizeof(addr)); */ if (t_bind(fd, &tbindstr, tres) < 0) { int oerrno = errno; const char *e_txt = krb5_get_error_message (context, errno); fprintf(stderr, gettext("%s: Cannot bind socket.\n"), whoami); fprintf(stderr, gettext("bind: %s\n"), e_txt); errno = oerrno; krb5_klog_syslog(LOG_ERR, gettext("Cannot bind socket: %s"), e_txt); if(oerrno == EADDRINUSE) { char *w = strrchr(whoami, '/'); if (w) { w++; } else { w = whoami; } fprintf(stderr, gettext( "This probably means that another %s process is already\n" "running, or that another program is using the server port (number %d)\n" "after being assigned it by the RPC portmap daemon. If another\n" "%s is already running, you should kill it before\n" "restarting the server. If, on the other hand, another program is\n" "using the server port, you should kill it before running\n" "%s, and ensure that the conflict does not occur in the\n" "future by making sure that %s is started on reboot\n" "before portmap.\n"), w, ntohs(addr.sin_port), w, w, w); krb5_klog_syslog(LOG_ERR, gettext("Check for already-running %s or for " "another process using port %d"), w, htons(addr.sin_port)); } kadm5_destroy(global_server_handle); krb5_klog_close(context); exit(1); } memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = htons(chgpw_params.kpasswd_port); if (bind(schpw, (struct sockaddr *)&addr, sizeof(addr)) < 0) { char portbuf[32]; int oerrno = errno; const char *e_txt = krb5_get_error_message (context, errno); fprintf(stderr, gettext("%s: Cannot bind socket.\n"), whoami); fprintf(stderr, gettext("bind: %s\n"), e_txt); errno = oerrno; (void) snprintf(portbuf, sizeof (portbuf), "%d", ntohs(addr.sin_port)); krb5_klog_syslog(LOG_ERR, gettext("cannot bind simple chpw socket: %s"), e_txt); if(oerrno == EADDRINUSE) { char *w = strrchr(whoami, '/'); if (w) { w++; } else { w = whoami; } fprintf(stderr, gettext( "This probably means that another %s process is already\n" "running, or that another program is using the server port (number %d).\n" "If another %s is already running, you should kill it before\n" "restarting the server.\n"), w, ntohs(addr.sin_port), w); } krb5_klog_close(context); exit(1); } transp = svc_tli_create(fd, nconf, NULL, 0, 0); (void) t_free((char *) tres, T_BIND); (void) endnetconfig(handlep); if(transp == NULL) { fprintf(stderr, gettext("%s: Cannot create RPC service.\n"), whoami); krb5_klog_syslog(LOG_ERR, gettext("Cannot create RPC service: %m")); kadm5_destroy(global_server_handle); krb5_klog_close(context); exit(1); } if(!svc_register(transp, KADM, KADMVERS, kadm_1, 0)) { fprintf(stderr, gettext("%s: Cannot register RPC service.\n"), whoami); krb5_klog_syslog(LOG_ERR, gettext("Cannot register RPC service, failing.")); kadm5_destroy(global_server_handle); krb5_klog_close(context); exit(1); } /* Solaris Kerberos: * The only service principals which matter here are * -> names[0].name (kadmin/) * -> names[1].name (changepw/) * KADM5_ADMIN_SERVICE_P, KADM5_CHANGEPW_SERVICE_P, * OVSEC_KADM_ADMIN_SERVICE_P, OVSEC_KADM_CHANGEPW_SERVICE_P * are all legacy service princs and calls to rpc_gss_set_svc_name() * using these principals will always fail as they are not host * based principals. */ if (ret = kadm5_get_adm_host_srv_name(context, params.realm, &names[0].name)) { krb5_klog_syslog(LOG_ERR, gettext("Cannot get host based service name for admin " "principal in realm %s: %s"), params.realm, error_message(ret)); fprintf(stderr, gettext("%s: Cannot get host based service name for admin " "principal in realm %s: %s\n"), whoami, params.realm, error_message(ret)); krb5_klog_close(context); exit(1); } if (ret = kadm5_get_cpw_host_srv_name(context, params.realm, &names[1].name)) { krb5_klog_syslog(LOG_ERR, gettext("Cannot get host based service name for changepw " "principal in realm %s: %s"), params.realm, error_message(ret)); fprintf(stderr, gettext("%s: Cannot get host based service name for " "changepw principal in realm %s: %s\n"), whoami, params.realm, error_message(ret)); krb5_klog_close(context); exit(1); } names[2].name = KADM5_ADMIN_SERVICE_P; names[3].name = KADM5_CHANGEPW_SERVICE_P; names[4].name = OVSEC_KADM_ADMIN_SERVICE_P; names[5].name = OVSEC_KADM_CHANGEPW_SERVICE_P; if (names[0].name == NULL || names[1].name == NULL || names[2].name == NULL || names[3].name == NULL || names[4].name == NULL || names[5].name == NULL) { krb5_klog_syslog(LOG_ERR, gettext("Cannot initialize GSS-API authentication, " "failing.")); fprintf(stderr, gettext("%s: Cannot initialize " "GSS-API authentication.\n"), whoami); krb5_klog_close(context); exit(1); } /* * Go through some contortions to point gssapi at a kdb keytab. * This prevents kadmind from needing to use an actual file-based * keytab. */ /* XXX extract kadm5's krb5_context */ hctx = ((kadm5_server_handle_t)global_server_handle)->context; /* Set ktkdb's internal krb5_context. */ ret = krb5_ktkdb_set_context(hctx); if (ret) { krb5_klog_syslog(LOG_ERR, "Can't set kdb keytab's internal context."); goto kterr; } /* Solaris Kerberos */ ret = krb5_db_set_mkey(hctx, &((kadm5_server_handle_t)global_server_handle)->master_keyblock); if (ret) { krb5_klog_syslog(LOG_ERR, "Can't set master key for kdb keytab."); goto kterr; } ret = krb5_kt_register(context, &krb5_kt_kdb_ops); if (ret) { krb5_klog_syslog(LOG_ERR, "Can't register kdb keytab."); goto kterr; } /* Tell gssapi about the kdb keytab. */ ret = krb5_gss_register_acceptor_identity("KDB:"); if (ret) { krb5_klog_syslog(LOG_ERR, "Can't register acceptor keytab."); goto kterr; } kterr: if (ret) { krb5_klog_syslog(LOG_ERR, "%s", krb5_get_error_message (context, ret)); fprintf(stderr, "%s: Can't set up keytab for RPC.\n", whoami); kadm5_destroy(global_server_handle); krb5_klog_close(context); exit(1); } /* * Try to acquire creds for the old OV services as well as the * new names, but if that fails just fall back on the new names. */ if (rpc_gss_set_svc_name(names[5].name, "kerberos_v5", 0, KADM, KADMVERS) && rpc_gss_set_svc_name(names[4].name, "kerberos_v5", 0, KADM, KADMVERS)) oldnames++; if (rpc_gss_set_svc_name(names[3].name, "kerberos_v5", 0, KADM, KADMVERS)) oldnames++; if (rpc_gss_set_svc_name(names[2].name, "kerberos_v5", 0, KADM, KADMVERS)) oldnames++; /* If rpc_gss_set_svc_name() fails for either kadmin/ or * for changepw/ then try to determine if this is caused * by a missing keytab file or entry. If so, log it and continue. */ if (rpc_gss_set_svc_name(names[0].name, "kerberos_v5", 0, KADM, KADMVERS)) oldnames++; if (rpc_gss_set_svc_name(names[1].name, "kerberos_v5", 0, KADM, KADMVERS)) oldnames++; retdn = getdomnames(context, params.realm, &dnames); if (retdn == 0 && dnames) { /* * Multi-homed KDCs sometimes may need to set svc names * for multiple net interfaces so we set them for * all interfaces just in case. */ set_svc_domnames(KADM5_ADMIN_HOST_SERVICE, dnames, KADM, KADMVERS); set_svc_domnames(KADM5_CHANGEPW_HOST_SERVICE, dnames, KADM, KADMVERS); } /* if set_names succeeded, this will too */ in_buf.value = names[1].name; in_buf.length = strlen(names[1].name) + 1; (void) gss_import_name(&OMret, &in_buf, (gss_OID) nt_krb5_name_oid, &gss_changepw_name); if (oldnames) { in_buf.value = names[3].name; in_buf.length = strlen(names[3].name) + 1; (void) gss_import_name(&OMret, &in_buf, (gss_OID) nt_krb5_name_oid, &gss_oldchangepw_name); } if ((ret = kadm5int_acl_init(context, 0, params.acl_file))) { errmsg = krb5_get_error_message (context, ret); krb5_klog_syslog(LOG_ERR, gettext("Cannot initialize acl file: %s"), errmsg); fprintf(stderr, gettext("%s: Cannot initialize acl file: %s\n"), whoami, errmsg); kadm5_destroy(global_server_handle); krb5_klog_close(context); exit(1); } /* * Solaris Kerberos: * Warn if the acl file contains an entry for a principal matching the * default (unconfigured) acl rule. */ gssbuf.length = strlen("x/admin@___default_realm___"); gssbuf.value = "x/admin@___default_realm___"; /* Use any value as the first component - 'x' in this case */ if (gss_import_name(&minor_status, &gssbuf, GSS_C_NT_USER_NAME, &name) == GSS_S_COMPLETE) { if (kadm5int_acl_check(context, name, ACL_MODIFY, NULL, NULL)) { krb5_klog_syslog(LOG_WARNING, gettext("acls may not be properly configured: " "found an acl matching \"___default_realm___\" in " " %s"), params.acl_file); (void) fprintf(stderr, gettext("%s: Warning: " "acls may not be properly configured: found an acl " "matching \"___default_realm___\" in %s\n"), whoami, params.acl_file); } (void) gss_release_name(&minor_status, &name); } gssbuf.value = NULL; gssbuf.length = 0; /* * Solaris Kerberos: * List the logs (FILE, STDERR, etc) which are currently being * logged to and print to stderr. Useful when trying to * track down a failure via SMF. */ if (ret = krb5_klog_list_logs(whoami)) { fprintf(stderr, gettext("%s: %s while listing logs\n"), whoami, error_message(ret)); krb5_klog_syslog(LOG_ERR, gettext("%s while listing logs"), error_message(ret)); } if (!nofork && (ret = daemon(0, 0))) { ret = errno; errmsg = krb5_get_error_message (context, ret); krb5_klog_syslog(LOG_ERR, gettext("Cannot detach from tty: %s"), errmsg); fprintf(stderr, gettext("%s: Cannot detach from tty: %s\n"), whoami, errmsg); kadm5_destroy(global_server_handle); krb5_klog_close(context); exit(1); } /* SUNW14resync */ #if 0 krb5_klog_syslog(LOG_INFO, "Seeding random number generator"); ret = krb5_c_random_os_entropy(context, 1, NULL); if (ret) { krb5_klog_syslog(LOG_ERR, "Error getting random seed: %s, aborting", krb5_get_error_message(context, ret)); kadm5_destroy(global_server_handle); krb5_klog_close(context); exit(1); } #endif handle = global_server_handle; ctx = handle->context; if (params.iprop_enabled == TRUE) { if (ret = krb5_db_supports_iprop(ctx, &iprop_supported)) { fprintf(stderr, gettext("%s: %s while trying to determine if KDB " "plugin supports iprop\n"), whoami, error_message(ret)); krb5_klog_syslog(LOG_ERR, gettext("%s while trying to determine if KDB " "plugin supports iprop"), error_message(ret)); krb5_klog_close(ctx); exit(1); } if (!iprop_supported) { fprintf(stderr, gettext("%s: Warning, current KDB " "plugin does not support iprop, continuing " "with iprop disabled\n"), whoami); krb5_klog_syslog(LOG_WARNING, gettext("Warning, current KDB " "plugin does not support iprop, continuing " "with iprop disabled")); ulog_set_role(ctx, IPROP_NULL); } else ulog_set_role(ctx, IPROP_MASTER); } else ulog_set_role(ctx, IPROP_NULL); log_ctx = ctx->kdblog_context; if (log_ctx && (log_ctx->iproprole == IPROP_MASTER)) { /* * IProp is enabled, so let's map in the update log * and setup the service. */ if (ret = ulog_map(ctx, ¶ms, FKADMIND)) { fprintf(stderr, gettext("%s: %s while mapping update log " "(`%s.ulog')\n"), whoami, error_message(ret), params.dbname); krb5_klog_syslog(LOG_ERR, gettext("%s while mapping update log " "(`%s.ulog')"), error_message(ret), params.dbname); krb5_klog_close(ctx); exit(1); } if (nofork) fprintf(stderr, "%s: create IPROP svc (PROG=%d, VERS=%d)\n", whoami, KRB5_IPROP_PROG, KRB5_IPROP_VERS); if (!svc_create(krb5_iprop_prog_1, KRB5_IPROP_PROG, KRB5_IPROP_VERS, "circuit_v")) { fprintf(stderr, gettext("%s: Cannot create IProp RPC service (PROG=%d, VERS=%d)\n"), whoami, KRB5_IPROP_PROG, KRB5_IPROP_VERS); krb5_klog_syslog(LOG_ERR, gettext("Cannot create IProp RPC service (PROG=%d, VERS=%d), failing."), KRB5_IPROP_PROG, KRB5_IPROP_VERS); krb5_klog_close(ctx); exit(1); } if (ret = kiprop_get_adm_host_srv_name(ctx, params.realm, &kiprop_name)) { krb5_klog_syslog(LOG_ERR, gettext("%s while getting IProp svc name, failing"), error_message(ret)); fprintf(stderr, gettext("%s: %s while getting IProp svc name, failing\n"), whoami, error_message(ret)); krb5_klog_close(ctx); exit(1); } if (!rpc_gss_set_svc_name(kiprop_name, "kerberos_v5", 0, KRB5_IPROP_PROG, KRB5_IPROP_VERS)) { rpc_gss_error_t err; (void) rpc_gss_get_error(&err); krb5_klog_syslog(LOG_ERR, gettext("Unable to set RPCSEC_GSS service name (`%s'), failing."), kiprop_name ? kiprop_name : ""); fprintf(stderr, gettext("%s: Unable to set RPCSEC_GSS service name (`%s'), failing.\n"), whoami, kiprop_name ? kiprop_name : ""); if (nofork) { fprintf(stderr, "%s: set svc name (rpcsec err=%d, sys err=%d)\n", whoami, err.rpc_gss_error, err.system_error); } exit(1); } free(kiprop_name); if (retdn == 0 && dnames) { set_svc_domnames(KADM5_KIPROP_HOST_SERVICE, dnames, KRB5_IPROP_PROG, KRB5_IPROP_VERS); } } else { if (!oldnames) { /* rpc_gss_set_svc_name failed for both kadmin/ and * changepw/. */ krb5_klog_syslog(LOG_ERR, gettext("Unable to set RPCSEC_GSS service names " "('%s, %s')"), names[0].name, names[1].name); fprintf(stderr, gettext("%s: Unable to set RPCSEC_GSS service names " "('%s, %s')\n"), whoami, names[0].name, names[1].name); krb5_klog_close(context); exit(1); } } if (dnames) freedomnames(dnames); setup_signal_handlers(log_ctx->iproprole); krb5_klog_syslog(LOG_INFO, gettext("starting")); if (nofork) fprintf(stderr, "%s: starting...\n", whoami); /* * We now call our own customized async event processing * function kadm_svc_run(), as opposed to svc_run() earlier, * since this enables kadmind to also listen-to/process * non-RPCSEC_GSS based change-pwd requests apart from the * regular, RPCSEC_GSS kpasswd requests from Solaris Krb5 clients. */ kadm_svc_run(); krb5_klog_syslog(LOG_INFO, gettext("finished, exiting")); kadm5_destroy(global_server_handle); t_close(fd); krb5_klog_close(context); exit(0); } /* * Function: kadm_svc_run * * Purpose: modified version of sunrpc svc_run. * which closes the database every TIMEOUT seconds. * * Arguments: * Requires: * Effects: * Modifies: */ void kadm_svc_run(void) { struct pollfd *rfd = 0; struct timeval timeout; int pollret; int nfds = 0; int i; while(signal_request_exit == 0) { timeout.tv_sec = TIMEOUT; timeout.tv_usec = 0; if (nfds != svc_max_pollfd) { rfd = realloc(rfd, sizeof (pollfd_t) * svc_max_pollfd); nfds = svc_max_pollfd; } (void) memcpy(rfd, svc_pollfd, sizeof (pollfd_t) * svc_max_pollfd); for (i = 0; i < nfds; i++) { if (rfd[i].fd == -1) { rfd[i].fd = schpw; rfd[i].events = POLLIN; break; } } switch(pollret = poll(rfd, nfds, __rpc_timeval_to_msec(&timeout))) { case -1: if(errno == EINTR) continue; perror("poll"); return; case 0: continue; default: for (i = 0; i < nfds; i++) { if (rfd[i].revents & POLLIN) { if (rfd[i].fd == schpw) handle_chpw(context, schpw, global_server_handle, &chgpw_params); else svc_getreq_poll(rfd, pollret); break; } else { if (i == (nfds - 1)) perror("poll"); } } break; } } } /* * Function: setup_signal_handlers * * Purpose: Setup signal handling functions with either * System V's signal() or POSIX_SIGNALS. */ void setup_signal_handlers(iprop_role iproprole) { #ifdef POSIX_SIGNALS (void) sigemptyset(&s_action.sa_mask); s_action.sa_handler = request_exit; (void) sigaction(SIGINT, &s_action, (struct sigaction *) NULL); (void) sigaction(SIGTERM, &s_action, (struct sigaction *) NULL); (void) sigaction(SIGQUIT, &s_action, (struct sigaction *) NULL); s_action.sa_handler = sig_pipe; (void) sigaction(SIGPIPE, &s_action, (struct sigaction *) NULL); /* * IProp will fork for a full-resync, we don't want to * wait on it and we don't want the living dead procs either. */ if (iproprole == IPROP_MASTER) { s_action.sa_handler = SIG_IGN; (void) sigaction(SIGCHLD, &s_action, (struct sigaction *) NULL); } #else signal(SIGINT, request_exit); signal(SIGTERM, request_exit); signal(SIGQUIT, request_exit); signal(SIGPIPE, sig_pipe); /* * IProp will fork for a full-resync, we don't want to * wait on it and we don't want the living dead procs either. */ if (iproprole == IPROP_MASTER) (void) signal(SIGCHLD, SIG_IGN); #endif /* POSIX_SIGNALS */ return; } /* * Function: request_exit * * Purpose: sets flags saying the server got a signal and that it * should exit when convient. * * Arguments: * Requires: * Effects: * modifies signal_request_exit which ideally makes the server exit * at some point. * * Modifies: * signal_request_exit */ void request_exit(int signum) { krb5_klog_syslog(LOG_NOTICE, gettext("Got signal to request exit")); signal_request_exit = 1; return; } /* * Function: sig_pipe * * Purpose: SIGPIPE handler * * Effects: krb5_klog_syslogs a message that a SIGPIPE occurred and returns, * thus causing the read() or write() to fail and, presumable, the RPC * to recover. Otherwise, the process aborts. */ void sig_pipe(int unused) { #ifndef POSIX_SIGNALS signal(SIGPIPE, sig_pipe); #endif /* POSIX_SIGNALS */ krb5_klog_syslog(LOG_NOTICE, gettext("Warning: Received a SIGPIPE; " "probably a client aborted. Continuing.")); return; } /* * 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 * */ #include #include "misc.h" /* * In server_stubs.c, kadmind has to be able to call kadm5 functions * with the arguments appropriate for any api version. Because of the * prototypes in admin.h, however, the compiler will only allow one * set of arguments to be passed. This file exports the old api * definitions with a different name, so they can be called from * server_stubs.c, and just passes on the call to the real api * function; it uses the old api version, however, so it can actually * call the real api functions whereas server_stubs.c cannot. * * This is most useful for functions like kadm5_get_principal that * take a different number of arguments based on API version. For * kadm5_get_policy, the same thing could be accomplished with * typecasts instead. */ kadm5_ret_t kadm5_get_principal_v1(void *server_handle, krb5_principal principal, kadm5_principal_ent_t_v1 *ent) { return kadm5_get_principal(server_handle, principal,(kadm5_principal_ent_t) ent, 0); } kadm5_ret_t kadm5_get_policy_v1(void *server_handle, kadm5_policy_t name, kadm5_policy_ent_t *ent) { return kadm5_get_policy(server_handle, name,(kadm5_policy_ent_t) ent); } /* * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. */ /* * 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 * */ /* * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved * */ #include #include #include /* for gss_nt_krb5_name */ #include #include #include #include #include #include /* inet_ntoa */ #include /* krb5_klog_syslog */ #include #include #include "misc.h" #define LOG_UNAUTH gettext("Unauthorized request: %s, %s, " \ "client=%s, service=%s, addr=%s") #define LOG_DONE gettext("Request: %s, %s, %s, client=%s, " \ "service=%s, addr=%s") extern gss_name_t gss_changepw_name; extern gss_name_t gss_oldchangepw_name; extern void * global_server_handle; extern short l_port; char buf[33]; #define CHANGEPW_SERVICE(rqstp) \ (cmp_gss_names_rel_1(acceptor_name(rqstp), gss_changepw_name) |\ (gss_oldchangepw_name && \ cmp_gss_names_rel_1(acceptor_name(rqstp), \ gss_oldchangepw_name))) static int gss_to_krb5_name(kadm5_server_handle_t handle, gss_name_t gss_name, krb5_principal *princ); static int gss_name_to_string(gss_name_t gss_name, gss_buffer_desc *str); static gss_name_t acceptor_name(struct svc_req * rqstp); kadm5_ret_t kadm5_get_priv(void *server_handle, long *privs, gss_name_t clnt); gss_name_t get_clnt_name(struct svc_req * rqstp) { OM_uint32 maj_stat, min_stat; gss_name_t name; rpc_gss_rawcred_t *raw_cred; void *cookie; gss_buffer_desc name_buff; rpc_gss_getcred(rqstp, &raw_cred, NULL, &cookie); name_buff.value = raw_cred->client_principal->name; name_buff.length = raw_cred->client_principal->len; maj_stat = gss_import_name(&min_stat, &name_buff, (gss_OID) GSS_C_NT_EXPORT_NAME, &name); if (maj_stat != GSS_S_COMPLETE) { return (NULL); } return (name); } char * client_addr(struct svc_req * req, char *buf) { struct sockaddr *ca; u_char *b; char *frontspace = " "; /* * Convert the caller's IP address to a dotted string */ ca = (struct sockaddr *) svc_getrpccaller(req->rq_xprt)->buf; if (ca->sa_family == AF_INET) { b = (u_char *) & ((struct sockaddr_in *) ca)->sin_addr; (void) sprintf(buf, "%s(%d.%d.%d.%d) ", frontspace, b[0] & 0xFF, b[1] & 0xFF, b[2] & 0xFF, b[3] & 0xFF); } else { /* * No IP address to print. If there was a host name * printed, then we print a space. */ (void) sprintf(buf, frontspace); } return (buf); } static int cmp_gss_names(gss_name_t n1, gss_name_t n2) { OM_uint32 emaj, emin; int equal; if (GSS_ERROR(emaj = gss_compare_name(&emin, n1, n2, &equal))) return(0); return(equal); } /* Does a comparison of the names and then releases the first entity */ /* For use above in CHANGEPW_SERVICE */ static int cmp_gss_names_rel_1(gss_name_t n1, gss_name_t n2) { OM_uint32 min_stat; int ret; ret = cmp_gss_names(n1, n2); if (n1) (void) gss_release_name(&min_stat, &n1); return ret; } /* * Function check_handle * * Purpose: Check a server handle and return a com_err code if it is * invalid or 0 if it is valid. * * Arguments: * * handle The server handle. */ static int check_handle(void *handle) { CHECK_HANDLE(handle); return 0; } /* * Function: new_server_handle * * Purpose: Constructs a server handle suitable for passing into the * server library API functions, by folding the client's API version * and calling principal into the server handle returned by * kadm5_init. * * Arguments: * api_version (input) The API version specified by the client * rqstp (input) The RPC request * handle (output) The returned handle * (output) An error code, or 0 if no error occurred * * Effects: * Returns a pointer to allocated storage containing the server * handle. If an error occurs, then no allocated storage is * returned, and the return value of the function will be a * non-zero com_err code. * * The allocated storage for the handle should be freed with * free_server_handle (see below) when it is no longer needed. */ static kadm5_ret_t new_server_handle(krb5_ui_4 api_version, struct svc_req *rqstp, kadm5_server_handle_t *out_handle) { kadm5_server_handle_t handle; gss_name_t name; OM_uint32 min_stat; if (! (handle = (kadm5_server_handle_t) malloc(sizeof(*handle)))) return ENOMEM; *handle = *(kadm5_server_handle_t)global_server_handle; handle->api_version = api_version; if (!(name = get_clnt_name(rqstp))) { free(handle); return KADM5_FAILURE; } if (! gss_to_krb5_name(handle, name, &handle->current_caller)) { free(handle); gss_release_name(&min_stat, &name); return KADM5_FAILURE; } gss_release_name(&min_stat, &name); *out_handle = handle; return 0; } /* * Function: free_server_handle * * Purpose: Free handle memory allocated by new_server_handle * * Arguments: * handle (input/output) The handle to free */ static void free_server_handle(kadm5_server_handle_t handle) { krb5_free_principal(handle->context, handle->current_caller); free(handle); } /* * Function: setup_gss_names * * Purpose: Create printable representations of the client and server * names. * * Arguments: * rqstp (r) the RPC request * client_name (w) pointer to client_name string * server_name (w) pointer to server_name string * * Effects: * * Unparses the client and server names into client_name and * server_name, both of which must be freed by the caller. Returns 0 * on success and -1 on failure. On failure client_name and server_name * will point to null. */ /* SUNW14resync */ int setup_gss_names(struct svc_req *rqstp, char **client_name, char **server_name) { OM_uint32 maj_stat, min_stat; rpc_gss_rawcred_t *raw_cred; gss_buffer_desc name_buf; char *tmp, *val; size_t len; gss_name_t name; *client_name = NULL; rpc_gss_getcred(rqstp, &raw_cred, NULL, NULL); /* Return a copy of the service principal from the raw_cred */ *server_name = strdup(raw_cred->svc_principal); if (*server_name == NULL) return (-1); if (!(name = get_clnt_name(rqstp))) { free(*server_name); *server_name = NULL; return (-1); } maj_stat = gss_display_name(&min_stat, name, &name_buf, NULL); if (maj_stat != GSS_S_COMPLETE) { free(*server_name); gss_release_name(&min_stat, &name); *server_name = NULL; return (-1); } gss_release_name(&min_stat, &name); /* * Allocate space to copy the client principal. We allocate an * extra byte to make the string null terminated if we need to. */ val = name_buf.value; len = name_buf.length + (val[name_buf.length - 1] != '\0'); /* len is the length including the null terminating byte. */ tmp = malloc(len); if (tmp) { memcpy(tmp, val, len - 1); tmp[len - 1] = '\0'; } else { free(*server_name); *server_name = NULL; } /* Were done with the GSS buffer */ (void) gss_release_buffer(&min_stat, &name_buf); *client_name = tmp; return (tmp ? 0 : -1); } static gss_name_t acceptor_name(struct svc_req * rqstp) { OM_uint32 maj_stat, min_stat; gss_name_t name; rpc_gss_rawcred_t *raw_cred; void *cookie; gss_buffer_desc name_buff; rpc_gss_getcred(rqstp, &raw_cred, NULL, &cookie); name_buff.value = raw_cred->svc_principal; name_buff.length = strlen(raw_cred->svc_principal); maj_stat = gss_import_name(&min_stat, &name_buff, (gss_OID) gss_nt_krb5_name, &name); if (maj_stat != GSS_S_COMPLETE) { gss_release_buffer(&min_stat, &name_buff); return (NULL); } maj_stat = gss_display_name(&min_stat, name, &name_buff, NULL); if (maj_stat != GSS_S_COMPLETE) { gss_release_buffer(&min_stat, &name_buff); return (NULL); } gss_release_buffer(&min_stat, &name_buff); return name; } static int cmp_gss_krb5_name(kadm5_server_handle_t handle, gss_name_t gss_name, krb5_principal princ) { krb5_principal princ2; int status; if (! gss_to_krb5_name(handle, gss_name, &princ2)) return 0; status = krb5_principal_compare(handle->context, princ, princ2); krb5_free_principal(handle->context, princ2); return status; } /* * This routine primarily validates the username and password * of the principal to be created, if a prior acl check for * the 'u' privilege succeeds. Validation is done using * the PAM `k5migrate' service. k5migrate normally stacks * pam_unix_auth.so and pam_unix_account.so in its auth and * account stacks respectively. * * Returns 1 (true), if validation is successful, * else returns 0 (false). */ int verify_pam_pw(char *userdata, char *pwd) { pam_handle_t *pamh; int err = 0; int result = 1; char *user = NULL; char *ptr = NULL; ptr = strchr(userdata, '@'); if (ptr != NULL) { user = (char *)malloc(ptr - userdata + 1); (void) strlcpy(user, userdata, (ptr - userdata) + 1); } else { user = (char *)strdup(userdata); } err = pam_start("k5migrate", user, NULL, &pamh); if (err != PAM_SUCCESS) { syslog(LOG_ERR, "verify_pam_pw: pam_start() failed, %s\n", pam_strerror(pamh, err)); if (user) free(user); return (0); } if (user) free(user); err = pam_set_item(pamh, PAM_AUTHTOK, (void *)pwd); if (err != PAM_SUCCESS) { syslog(LOG_ERR, "verify_pam_pw: pam_set_item() failed, %s\n", pam_strerror(pamh, err)); (void) pam_end(pamh, err); return (0); } err = pam_authenticate(pamh, PAM_SILENT); if (err != PAM_SUCCESS) { syslog(LOG_ERR, "verify_pam_pw: pam_authenticate() " "failed, %s\n", pam_strerror(pamh, err)); (void) pam_end(pamh, err); return (0); } err = pam_acct_mgmt(pamh, PAM_SILENT); if (err != PAM_SUCCESS) { syslog(LOG_ERR, "verify_pam_pw: pam_acct_mgmt() failed, %s\n", pam_strerror(pamh, err)); (void) pam_end(pamh, err); return (0); } (void) pam_end(pamh, PAM_SUCCESS); return (result); } static int gss_to_krb5_name(kadm5_server_handle_t handle, gss_name_t gss_name, krb5_principal *princ) { OM_uint32 status, minor_stat; gss_buffer_desc gss_str; gss_OID gss_type; int success; status = gss_display_name(&minor_stat, gss_name, &gss_str, &gss_type); if ((status != GSS_S_COMPLETE) || (!g_OID_equal(gss_type, gss_nt_krb5_name))) return 0; success = (krb5_parse_name(handle->context, gss_str.value, princ) == 0); gss_release_buffer(&minor_stat, &gss_str); return success; } static int gss_name_to_string(gss_name_t gss_name, gss_buffer_desc *str) { OM_uint32 status, minor_stat; gss_OID gss_type; status = gss_display_name(&minor_stat, gss_name, str, &gss_type); if ((status != GSS_S_COMPLETE) || (gss_type != gss_nt_krb5_name)) return 1; return 0; } static int log_unauth( char *op, char *target, char *client, char *server, char *addr) { size_t tlen, clen, slen; char *tdots, *cdots, *sdots; tlen = strlen(target); trunc_name(&tlen, &tdots); clen = strlen(client); trunc_name(&clen, &cdots); slen = strlen(server); trunc_name(&slen, &sdots); return krb5_klog_syslog(LOG_NOTICE, "Unauthorized request: %s, %.*s%s, " "client=%.*s%s, service=%.*s%s, addr=%s", op, tlen, target, tdots, clen, client, cdots, slen, server, sdots, addr); } static int log_done( char *op, char *target, const char *errmsg, char *client, char *server, char *addr) { size_t tlen, clen, slen; char *tdots, *cdots, *sdots; tlen = strlen(target); trunc_name(&tlen, &tdots); clen = strlen(client); trunc_name(&clen, &cdots); slen = strlen(server); trunc_name(&slen, &sdots); return krb5_klog_syslog(LOG_NOTICE, "Request: %s, %.*s%s, %s, " "client=%.*s%s, service=%.*s%s, addr=%s", op, tlen, target, tdots, errmsg, clen, client, cdots, slen, server, sdots, addr); } generic_ret * create_principal_2_svc(cprinc_arg *arg, struct svc_req *rqstp) { static generic_ret ret; char *prime_arg = NULL; char *client_name = NULL, *service_name = NULL; int policy_migrate = 0; OM_uint32 minor_stat; kadm5_server_handle_t handle; kadm5_ret_t retval; restriction_t *rp; const char *errmsg = NULL; gss_name_t name = NULL; xdr_free(xdr_generic_ret, (char *) &ret); if ((ret.code = new_server_handle(arg->api_version, rqstp, &handle))) return &ret; if ((ret.code = check_handle((void *)handle))) goto error; ret.api_version = handle->api_version; if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { ret.code = KADM5_FAILURE; goto error; } if (krb5_unparse_name(handle->context, arg->rec.principal, &prime_arg)) { ret.code = KADM5_BAD_PRINCIPAL; goto error; } if (!(name = get_clnt_name(rqstp))) { ret.code = KADM5_FAILURE; goto error; } if (kadm5int_acl_check(handle->context, name, ACL_MIGRATE, arg->rec.principal, &rp) && verify_pam_pw(prime_arg, arg->passwd)) { policy_migrate = 1; } if (CHANGEPW_SERVICE(rqstp) || (!kadm5int_acl_check(handle->context, name, ACL_ADD, arg->rec.principal, &rp) && !(policy_migrate)) || kadm5int_acl_impose_restrictions(handle->context, &arg->rec, &arg->mask, rp)) { ret.code = KADM5_AUTH_ADD; audit_kadmind_unauth(rqstp->rq_xprt, l_port, "kadm5_create_principal", prime_arg, client_name); log_unauth("kadm5_create_principal", prime_arg, client_name, service_name, client_addr(rqstp, buf)); } else { ret.code = kadm5_create_principal((void *)handle, &arg->rec, arg->mask, arg->passwd); /* Solaris Kerberos */ if( ret.code != 0 ) errmsg = krb5_get_error_message(handle ? handle->context : NULL, ret.code); audit_kadmind_auth(rqstp->rq_xprt, l_port, "kadm5_create_principal", prime_arg, client_name, ret.code); log_done("kadm5_create_principal", prime_arg, errmsg ? errmsg : "success", client_name, service_name, client_addr(rqstp, buf)); if (errmsg != NULL) krb5_free_error_message(handle ? handle->context : NULL, errmsg); if (policy_migrate && (ret.code == 0)) { arg->rec.policy = strdup("default"); if ((arg->mask & KADM5_PW_EXPIRATION)) { arg->mask = 0; arg->mask |= KADM5_POLICY; arg->mask |= KADM5_PW_EXPIRATION; } else { arg->mask = 0; arg->mask |= KADM5_POLICY; } retval = kadm5_modify_principal((void *)handle, &arg->rec, arg->mask); log_done("kadm5_modify_principal", prime_arg, ((retval == 0) ? "success" : error_message(retval)), client_name, service_name, client_addr(rqstp, buf)); } } error: if (name) gss_release_name(&minor_stat, &name); free_server_handle(handle); if (prime_arg) free(prime_arg); if (client_name) free(client_name); if (service_name) free(service_name); return (&ret); } generic_ret * create_principal3_2_svc(cprinc3_arg *arg, struct svc_req *rqstp) { static generic_ret ret; char *prime_arg = NULL; char *client_name = NULL, *service_name = NULL; int policy_migrate = 0; OM_uint32 minor_stat; kadm5_server_handle_t handle; kadm5_ret_t retval; restriction_t *rp; const char *errmsg = NULL; gss_name_t name = NULL; xdr_free(xdr_generic_ret, (char *) &ret); if ((ret.code = new_server_handle(arg->api_version, rqstp, &handle))) return &ret; if ((ret.code = check_handle((void *)handle))) goto error; ret.api_version = handle->api_version; if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { ret.code = KADM5_FAILURE; goto error; } if (krb5_unparse_name(handle->context, arg->rec.principal, &prime_arg)) { ret.code = KADM5_BAD_PRINCIPAL; goto error; } if (!(name = get_clnt_name(rqstp))) { ret.code = KADM5_FAILURE; goto error; } if (kadm5int_acl_check(handle->context, name, ACL_MIGRATE, arg->rec.principal, &rp) && verify_pam_pw(prime_arg, arg->passwd)) { policy_migrate = 1; } if (CHANGEPW_SERVICE(rqstp) || (!kadm5int_acl_check(handle->context, name, ACL_ADD, arg->rec.principal, &rp) && !(policy_migrate)) || kadm5int_acl_impose_restrictions(handle->context, &arg->rec, &arg->mask, rp)) { ret.code = KADM5_AUTH_ADD; log_unauth("kadm5_create_principal", prime_arg, client_name, service_name, client_addr(rqstp, buf)); } else { ret.code = kadm5_create_principal_3((void *)handle, &arg->rec, arg->mask, arg->n_ks_tuple, arg->ks_tuple, arg->passwd); /* Solaris Kerberos */ if( ret.code != 0 ) errmsg = krb5_get_error_message(handle ? handle->context : NULL, ret.code); log_done("kadm5_create_principal", prime_arg, errmsg ? errmsg : "success", client_name, service_name, client_addr(rqstp, buf)); if (errmsg != NULL) krb5_free_error_message(handle ? handle->context : NULL, errmsg); if (policy_migrate && (ret.code == 0)) { arg->rec.policy = strdup("default"); if ((arg->mask & KADM5_PW_EXPIRATION)) { arg->mask = 0; arg->mask |= KADM5_POLICY; arg->mask |= KADM5_PW_EXPIRATION; } else { arg->mask = 0; arg->mask |= KADM5_POLICY; } retval = kadm5_modify_principal((void *)handle, &arg->rec, arg->mask); log_done("kadm5_modify_principal", prime_arg, ((retval == 0) ? "success" : error_message(retval)), client_name, service_name, client_addr(rqstp, buf)); } } error: if (name) gss_release_name(&minor_stat, &name); free_server_handle(handle); if (client_name) free(client_name); if (service_name) free(service_name); if (prime_arg) free(prime_arg); return &ret; } generic_ret * delete_principal_2_svc(dprinc_arg *arg, struct svc_req *rqstp) { static generic_ret ret; char *prime_arg = NULL; char *client_name = NULL, *service_name = NULL; OM_uint32 min_stat; kadm5_server_handle_t handle; const char *errmsg = NULL; gss_name_t name = NULL; xdr_free(xdr_generic_ret, (char *) &ret); if ((ret.code = new_server_handle(arg->api_version, rqstp, &handle))) return &ret; if ((ret.code = check_handle((void *)handle))) goto error; ret.api_version = handle->api_version; if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { ret.code = KADM5_FAILURE; goto error; } if (krb5_unparse_name(handle->context, arg->princ, &prime_arg)) { ret.code = KADM5_BAD_PRINCIPAL; goto error; } if (!(name = get_clnt_name(rqstp))) { ret.code = KADM5_FAILURE; goto error; } if (CHANGEPW_SERVICE(rqstp) || !kadm5int_acl_check(handle->context, name, ACL_DELETE, arg->princ, NULL)) { ret.code = KADM5_AUTH_DELETE; audit_kadmind_unauth(rqstp->rq_xprt, l_port, "kadm5_delete_principal", prime_arg, client_name); log_unauth("kadm5_delete_principal", prime_arg, client_name, service_name, client_addr(rqstp, buf)); } else { ret.code = kadm5_delete_principal((void *)handle, arg->princ); /* Solaris Kerberos */ if( ret.code != 0 ) errmsg = krb5_get_error_message(handle ? handle->context : NULL, ret.code); audit_kadmind_auth(rqstp->rq_xprt, l_port, "kadm5_delete_principal", prime_arg, client_name, ret.code); log_done("kadm5_delete_principal", prime_arg, errmsg ? errmsg : "success", client_name, service_name, client_addr(rqstp, buf)); if (errmsg != NULL) krb5_free_error_message(handle ? handle->context : NULL, errmsg); } error: if (name) gss_release_name(&min_stat, &name); if (prime_arg) free(prime_arg); free_server_handle(handle); if (client_name) free(client_name); if (service_name) free(service_name); return &ret; } generic_ret * modify_principal_2_svc(mprinc_arg *arg, struct svc_req *rqstp) { static generic_ret ret; char *prime_arg = NULL; char *client_name = NULL, *service_name = NULL; OM_uint32 min_stat; kadm5_server_handle_t handle; restriction_t *rp; gss_name_t name = NULL; const char *errmsg = NULL; xdr_free(xdr_generic_ret, (char *) &ret); if ((ret.code = new_server_handle(arg->api_version, rqstp, &handle))) return &ret; if ((ret.code = check_handle((void *)handle))) goto error; if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { ret.code = KADM5_FAILURE; goto error; } if (krb5_unparse_name(handle->context, arg->rec.principal, &prime_arg)) { ret.code = KADM5_BAD_PRINCIPAL; goto error; } if (!(name = get_clnt_name(rqstp))) { ret.code = KADM5_FAILURE; goto error; } if (CHANGEPW_SERVICE(rqstp) || !kadm5int_acl_check(handle->context, name, ACL_MODIFY, arg->rec.principal, &rp) || kadm5int_acl_impose_restrictions(handle->context, &arg->rec, &arg->mask, rp)) { ret.code = KADM5_AUTH_MODIFY; audit_kadmind_unauth(rqstp->rq_xprt, l_port, "kadm5_modify_principal", prime_arg, client_name); log_unauth("kadm5_modify_principal", prime_arg, client_name, service_name, client_addr(rqstp, buf)); } else { ret.code = kadm5_modify_principal((void *)handle, &arg->rec, arg->mask); /* Solaris Kerberos */ if( ret.code != 0 ) errmsg = krb5_get_error_message(handle ? handle->context : NULL, ret.code); audit_kadmind_auth(rqstp->rq_xprt, l_port, "kadm5_modify_principal", prime_arg, client_name, ret.code); log_done("kadm5_modify_principal", prime_arg, errmsg ? errmsg : "success", client_name, service_name, client_addr(rqstp, buf)); if (errmsg != NULL) krb5_free_error_message(handle ? handle->context : NULL, errmsg); } error: if (name) gss_release_name(&min_stat, &name); free_server_handle(handle); if (prime_arg) free(prime_arg); if (client_name) free(client_name); if (service_name) free(service_name); return &ret; } generic_ret * rename_principal_2_svc(rprinc_arg *arg, struct svc_req *rqstp) { static generic_ret ret; char *prime_arg1 = NULL, *prime_arg2 = NULL; char prime_arg[BUFSIZ]; char *client_name = NULL, *service_name = NULL; OM_uint32 min_stat; kadm5_server_handle_t handle; restriction_t *rp; const char *errmsg = NULL; gss_name_t name = NULL; size_t tlen1, tlen2, clen, slen; char *tdots1, *tdots2, *cdots, *sdots; xdr_free(xdr_generic_ret, (char *) &ret); if ((ret.code = new_server_handle(arg->api_version, rqstp, &handle))) return &ret; if ((ret.code = check_handle((void *)handle))) goto error; if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { ret.code = KADM5_FAILURE; goto error; } if (krb5_unparse_name(handle->context, arg->src, &prime_arg1) || krb5_unparse_name(handle->context, arg->dest, &prime_arg2)) { ret.code = KADM5_BAD_PRINCIPAL; goto error; } tlen1 = strlen(prime_arg1); trunc_name(&tlen1, &tdots1); tlen2 = strlen(prime_arg2); trunc_name(&tlen2, &tdots2); clen = strlen(client_name); trunc_name(&clen, &cdots); slen = strlen(service_name); trunc_name(&slen, &sdots); (void) snprintf(prime_arg, sizeof (prime_arg), "%.*s%s to %.*s*s", tlen1, prime_arg1, tdots1, tlen2, prime_arg2, tdots2); ret.code = KADM5_OK; if (!(name = get_clnt_name(rqstp))) { ret.code = KADM5_FAILURE; goto error; } if (! CHANGEPW_SERVICE(rqstp)) { if (!kadm5int_acl_check(handle->context, name, ACL_DELETE, arg->src, NULL)) ret.code = KADM5_AUTH_DELETE; /* any restrictions at all on the ADD kills the RENAME */ if (!kadm5int_acl_check(handle->context, name, ACL_ADD, arg->dest, &rp)) { if (ret.code == KADM5_AUTH_DELETE) ret.code = KADM5_AUTH_INSUFFICIENT; else ret.code = KADM5_AUTH_ADD; } } else ret.code = KADM5_AUTH_INSUFFICIENT; if (ret.code != KADM5_OK) { audit_kadmind_unauth(rqstp->rq_xprt, l_port, "kadm5_rename_principal", prime_arg, client_name); krb5_klog_syslog(LOG_NOTICE, "Unauthorized request: kadm5_rename_principal, " "%.*s%s to %.*s%s, " "client=%.*s%s, service=%.*s%s, addr=%s", tlen1, prime_arg1, tdots1, tlen2, prime_arg2, tdots2, clen, client_name, cdots, slen, service_name, sdots, client_addr(rqstp, buf)); } else { ret.code = kadm5_rename_principal((void *)handle, arg->src, arg->dest); /* Solaris Kerberos */ if( ret.code != 0 ) errmsg = krb5_get_error_message(handle ? handle->context : NULL, ret.code); audit_kadmind_auth(rqstp->rq_xprt, l_port, "kadm5_rename_principal", prime_arg, client_name, ret.code); krb5_klog_syslog(LOG_NOTICE, "Request: kadm5_rename_principal, " "%.*s%s to %.*s%s, %s, " "client=%.*s%s, service=%.*s%s, addr=%s", tlen1, prime_arg1, tdots1, tlen2, prime_arg2, tdots2, errmsg ? errmsg : "success", clen, client_name, cdots, slen, service_name, sdots, client_addr(rqstp, buf)); if (errmsg != NULL) krb5_free_error_message(handle ? handle->context : NULL, errmsg); } error: if (name) gss_release_name(&min_stat, &name); free_server_handle(handle); if (prime_arg1) free(prime_arg1); if (prime_arg2) free(prime_arg2); if (client_name) free(client_name); if (service_name) free(service_name); return &ret; } gprinc_ret * get_principal_2_svc(gprinc_arg *arg, struct svc_req *rqstp) { static gprinc_ret ret; kadm5_principal_ent_t_v1 e; char *prime_arg = NULL, *funcname; char *client_name = NULL, *service_name = NULL; OM_uint32 min_stat; kadm5_server_handle_t handle; const char *errmsg = NULL; gss_name_t name = NULL; xdr_free(xdr_gprinc_ret, (char *) &ret); if ((ret.code = new_server_handle(arg->api_version, rqstp, &handle))) return &ret; if ((ret.code = check_handle((void *)handle))) goto error; ret.api_version = handle->api_version; funcname = handle->api_version == KADM5_API_VERSION_1 ? "kadm5_get_principal (V1)" : "kadm5_get_principal"; if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { ret.code = KADM5_FAILURE; goto error; } if (krb5_unparse_name(handle->context, arg->princ, &prime_arg)) { ret.code = KADM5_BAD_PRINCIPAL; goto error; } if (!(name = get_clnt_name(rqstp))) { ret.code = KADM5_FAILURE; goto error; } if (! cmp_gss_krb5_name(handle, name, arg->princ) && (CHANGEPW_SERVICE(rqstp) || !kadm5int_acl_check(handle->context, name, ACL_INQUIRE, arg->princ, NULL))) { ret.code = KADM5_AUTH_GET; audit_kadmind_unauth(rqstp->rq_xprt, l_port, funcname, prime_arg, client_name); log_unauth(funcname, prime_arg, client_name, service_name, client_addr(rqstp, buf)); } else { if (handle->api_version == KADM5_API_VERSION_1) { ret.code = kadm5_get_principal_v1((void *)handle, arg->princ, &e); if(ret.code == KADM5_OK) { memcpy(&ret.rec, e, sizeof(kadm5_principal_ent_rec_v1)); free(e); } } else { ret.code = kadm5_get_principal((void *)handle, arg->princ, &ret.rec, arg->mask); } /* Solaris Kerberos */ if( ret.code != 0 ) errmsg = krb5_get_error_message(handle ? handle->context : NULL, ret.code); audit_kadmind_auth(rqstp->rq_xprt, l_port, funcname, prime_arg, client_name, ret.code); log_done(funcname, prime_arg, errmsg ? errmsg : "success", client_name, service_name, client_addr(rqstp, buf)); if (errmsg != NULL) krb5_free_error_message(handle ? handle->context : NULL, errmsg); } error: if (name) gss_release_name(&min_stat, &name); free_server_handle(handle); if (prime_arg) free(prime_arg); if (client_name) free(client_name); if (service_name) free(service_name); return &ret; } gprincs_ret * get_princs_2_svc(gprincs_arg *arg, struct svc_req *rqstp) { static gprincs_ret ret; char *prime_arg = NULL; char *client_name = NULL, *service_name = NULL; OM_uint32 min_stat; kadm5_server_handle_t handle; gss_name_t name = NULL; const char *errmsg = NULL; xdr_free(xdr_gprincs_ret, (char *) &ret); if ((ret.code = new_server_handle(arg->api_version, rqstp, &handle))) return &ret; if ((ret.code = check_handle((void *)handle))) goto error; ret.api_version = handle->api_version; if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { ret.code = KADM5_FAILURE; goto error; } prime_arg = arg->exp; if (prime_arg == NULL) prime_arg = "*"; if (!(name = get_clnt_name(rqstp))) { ret.code = KADM5_FAILURE; goto error; } if (CHANGEPW_SERVICE(rqstp) || !kadm5int_acl_check(handle->context, name, ACL_LIST, NULL, NULL)) { ret.code = KADM5_AUTH_LIST; audit_kadmind_unauth(rqstp->rq_xprt, l_port, "kadm5_get_principals", prime_arg, client_name); log_unauth("kadm5_get_principals", prime_arg, client_name, service_name, client_addr(rqstp, buf)); } else { ret.code = kadm5_get_principals((void *)handle, arg->exp, &ret.princs, &ret.count); /* Solaris Kerberos */ if( ret.code != 0 ) errmsg = krb5_get_error_message(handle ? handle->context : NULL, ret.code); audit_kadmind_auth(rqstp->rq_xprt, l_port, "kadm5_get_principals", prime_arg, client_name, ret.code); log_done("kadm5_get_principals", prime_arg, errmsg ? errmsg : "success", client_name, service_name, client_addr(rqstp, buf)); if (errmsg != NULL) krb5_free_error_message(handle ? handle->context : NULL, errmsg); } error: if (name) gss_release_name(&min_stat, &name); free_server_handle(handle); if (client_name) free(client_name); if (service_name) free(service_name); return (&ret); } generic_ret * chpass_principal_2_svc(chpass_arg *arg, struct svc_req *rqstp) { static generic_ret ret; char *prime_arg = NULL; char *client_name = NULL, *service_name = NULL; OM_uint32 min_stat; kadm5_server_handle_t handle; const char *errmsg = NULL; gss_name_t name = NULL; xdr_free(xdr_generic_ret, (char *) &ret); if ((ret.code = new_server_handle(arg->api_version, rqstp, &handle))) return &ret; if ((ret.code = check_handle((void *)handle))) goto error; ret.api_version = handle->api_version; if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { ret.code = KADM5_FAILURE; goto error; } if (krb5_unparse_name(handle->context, arg->princ, &prime_arg)) { ret.code = KADM5_BAD_PRINCIPAL; goto error; } if (!(name = get_clnt_name(rqstp))) { ret.code = KADM5_FAILURE; goto error; } if (cmp_gss_krb5_name(handle, name, arg->princ)) { ret.code = chpass_principal_wrapper_3((void *)handle, arg->princ, FALSE, 0, NULL, arg->pass); } else if (!(CHANGEPW_SERVICE(rqstp)) && kadm5int_acl_check(handle->context, name, ACL_CHANGEPW, arg->princ, NULL)) { ret.code = kadm5_chpass_principal((void *)handle, arg->princ, arg->pass); } else { audit_kadmind_unauth(rqstp->rq_xprt, l_port, "kadm5_chpass_principal", prime_arg, client_name); log_unauth("kadm5_chpass_principal", prime_arg, client_name, service_name, client_addr(rqstp, buf)); ret.code = KADM5_AUTH_CHANGEPW; } if(ret.code != KADM5_AUTH_CHANGEPW) { /* Solaris Kerberos */ if( ret.code != 0 ) errmsg = krb5_get_error_message(handle ? handle->context : NULL, ret.code); audit_kadmind_auth(rqstp->rq_xprt, l_port, "kadm5_chpass_principal", prime_arg, client_name, ret.code); log_done("kadm5_chpass_principal", prime_arg, errmsg ? errmsg : "success", client_name, service_name, client_addr(rqstp, buf)); if (errmsg != NULL) krb5_free_error_message(handle ? handle->context : NULL, errmsg); } error: if (name) gss_release_name(&min_stat, &name); free_server_handle(handle); if (prime_arg) free(prime_arg); if (client_name) free(client_name); if (service_name) free(service_name); return (&ret); } generic_ret * chpass_principal3_2_svc(chpass3_arg *arg, struct svc_req *rqstp) { static generic_ret ret; char *prime_arg = NULL; char *client_name = NULL, *service_name = NULL; OM_uint32 min_stat; kadm5_server_handle_t handle; const char *errmsg = NULL; gss_name_t name = NULL; xdr_free(xdr_generic_ret, (char *) &ret); if ((ret.code = new_server_handle(arg->api_version, rqstp, &handle))) return &ret; if ((ret.code = check_handle((void *)handle))) goto error; ret.api_version = handle->api_version; if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { ret.code = KADM5_FAILURE; goto error; } if (krb5_unparse_name(handle->context, arg->princ, &prime_arg)) { ret.code = KADM5_BAD_PRINCIPAL; goto error; } if (!(name = get_clnt_name(rqstp))) { ret.code = KADM5_FAILURE; goto error; } if (cmp_gss_krb5_name(handle, name, arg->princ)) { ret.code = chpass_principal_wrapper_3((void *)handle, arg->princ, arg->keepold, arg->n_ks_tuple, arg->ks_tuple, arg->pass); } else if (!(CHANGEPW_SERVICE(rqstp)) && kadm5int_acl_check(handle->context, name, ACL_CHANGEPW, arg->princ, NULL)) { ret.code = kadm5_chpass_principal_3((void *)handle, arg->princ, arg->keepold, arg->n_ks_tuple, arg->ks_tuple, arg->pass); } else { log_unauth("kadm5_chpass_principal", prime_arg, client_name, service_name, client_addr(rqstp, buf)); ret.code = KADM5_AUTH_CHANGEPW; } if(ret.code != KADM5_AUTH_CHANGEPW) { /* Solaris Kerberos */ if( ret.code != 0 ) errmsg = krb5_get_error_message(handle ? handle->context : NULL, ret.code); log_done("kadm5_chpass_principal", prime_arg, errmsg ? errmsg : "success", client_name, service_name, client_addr(rqstp, buf)); if (errmsg != NULL) krb5_free_error_message(handle ? handle->context : NULL, errmsg); } error: if (name) gss_release_name(&min_stat, &name); free_server_handle(handle); if (client_name) free(client_name); if (service_name) free(service_name); if (prime_arg) free(prime_arg); return (&ret); } #ifdef SUNWOFF generic_ret * setv4key_principal_2_svc(setv4key_arg *arg, struct svc_req *rqstp) { static generic_ret ret; char *prime_arg = NULL; char *client_name = NULL, *service_name = NULL; OM_uint32 min_stat; kadm5_server_handle_t handle; const char *errmsg = NULL; gss_name_t name = NULL; xdr_free(xdr_generic_ret, (char *) &ret); if ((ret.code = new_server_handle(arg->api_version, rqstp, &handle))) return &ret; if ((ret.code = check_handle((void *)handle))) goto error; ret.api_version = handle->api_version; if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { ret.code = KADM5_FAILURE; goto error; } if (krb5_unparse_name(handle->context, arg->princ, &prime_arg)) { ret.code = KADM5_BAD_PRINCIPAL; goto error; } if (!(name = get_clnt_name(rqstp))) { ret.code = KADM5_FAILURE; goto error; } if (!(CHANGEPW_SERVICE(rqstp)) && kadm5int_acl_check(handle->context, name, ACL_SETKEY, arg->princ, NULL)) { ret.code = kadm5_setv4key_principal((void *)handle, arg->princ, arg->keyblock); } else { log_unauth("kadm5_setv4key_principal", prime_arg, client_name, service_name, client_addr(rqstp, buf)); ret.code = KADM5_AUTH_SETKEY; } if(ret.code != KADM5_AUTH_SETKEY) { /* Solaris Kerberos */ if( ret.code != 0 ) errmsg = krb5_get_error_message(handle ? handle->context : NULL, ret.code); log_done("kadm5_setv4key_principal", prime_arg, errmsg ? errmsg : "success", client_name, service_name, client_addr(rqstp, buf)); if (errmsg != NULL) krb5_free_error_message(handle ? handle->context : NULL, errmsg); } error: if (name) gss_release_name(&min_stat, &name); free_server_handle(handle); if (client_name) free(client_name); if (service_name) free(service_name); if (prime_arg) free(prime_arg); return (&ret); } #endif generic_ret * setkey_principal_2_svc(setkey_arg *arg, struct svc_req *rqstp) { static generic_ret ret; char *prime_arg; char *client_name, *service_name; OM_uint32 min_stat; kadm5_server_handle_t handle; const char *errmsg = NULL; gss_name_t name; xdr_free(xdr_generic_ret, (char *) &ret); if ((ret.code = new_server_handle(arg->api_version, rqstp, &handle))) return &ret; if ((ret.code = check_handle((void *)handle))) goto error; ret.api_version = handle->api_version; if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { ret.code = KADM5_FAILURE; goto error; } if (krb5_unparse_name(handle->context, arg->princ, &prime_arg)) { ret.code = KADM5_BAD_PRINCIPAL; goto error; } if (!(name = get_clnt_name(rqstp))) { ret.code = KADM5_FAILURE; goto error; } if (!(CHANGEPW_SERVICE(rqstp)) && kadm5int_acl_check(handle->context, name, ACL_SETKEY, arg->princ, NULL)) { ret.code = kadm5_setkey_principal((void *)handle, arg->princ, arg->keyblocks, arg->n_keys); } else { log_unauth("kadm5_setkey_principal", prime_arg, client_name, service_name, client_addr(rqstp, buf)); ret.code = KADM5_AUTH_SETKEY; } if(ret.code != KADM5_AUTH_SETKEY) { /* Solaris Kerberos */ if( ret.code != 0 ) errmsg = krb5_get_error_message(handle ? handle->context : NULL, ret.code); log_done("kadm5_setkey_principal", prime_arg, errmsg ? errmsg : "success", client_name, service_name, client_addr(rqstp, buf)); if (errmsg != NULL) krb5_free_error_message(handle ? handle->context : NULL, errmsg); } error: if (name) gss_release_name(&min_stat, &name); free_server_handle(handle); if (client_name) free(client_name); if (service_name) free(service_name); if (prime_arg) free(prime_arg); return (&ret); } generic_ret * setkey_principal3_2_svc(setkey3_arg *arg, struct svc_req *rqstp) { static generic_ret ret; char *prime_arg = NULL; char *client_name = NULL, *service_name = NULL; OM_uint32 min_stat; kadm5_server_handle_t handle; const char *errmsg = NULL; gss_name_t name = NULL; xdr_free(xdr_generic_ret, (char *) &ret); if ((ret.code = new_server_handle(arg->api_version, rqstp, &handle))) return &ret; if ((ret.code = check_handle((void *)handle))) goto error; ret.api_version = handle->api_version; if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { ret.code = KADM5_FAILURE; goto error; } if (krb5_unparse_name(handle->context, arg->princ, &prime_arg)) { ret.code = KADM5_BAD_PRINCIPAL; goto error; } if (!(name = get_clnt_name(rqstp))) { ret.code = KADM5_FAILURE; goto error; } if (!(CHANGEPW_SERVICE(rqstp)) && kadm5int_acl_check(handle->context, name, ACL_SETKEY, arg->princ, NULL)) { ret.code = kadm5_setkey_principal_3((void *)handle, arg->princ, arg->keepold, arg->n_ks_tuple, arg->ks_tuple, arg->keyblocks, arg->n_keys); } else { log_unauth("kadm5_setkey_principal", prime_arg, client_name, service_name, client_addr(rqstp, buf)); ret.code = KADM5_AUTH_SETKEY; } if(ret.code != KADM5_AUTH_SETKEY) { /* Solaris Kerberos */ if( ret.code != 0 ) errmsg = krb5_get_error_message(handle ? handle->context : NULL, ret.code); log_done("kadm5_setkey_principal", prime_arg, errmsg ? errmsg : "success", client_name, service_name, client_addr(rqstp, buf)); if (errmsg != NULL) krb5_free_error_message(handle ? handle->context : NULL, errmsg); } error: if (name) gss_release_name(&min_stat, &name); free_server_handle(handle); if (client_name) free(client_name); if (service_name) free(service_name); if (prime_arg) free(prime_arg); return &ret; } chrand_ret * chrand_principal_2_svc(chrand_arg *arg, struct svc_req *rqstp) { static chrand_ret ret; krb5_keyblock *k; int nkeys; char *prime_arg = NULL, *funcname; char *client_name = NULL, *service_name = NULL; OM_uint32 min_stat; kadm5_server_handle_t handle; const char *errmsg = NULL; gss_name_t name = NULL; xdr_free(xdr_chrand_ret, (char *) &ret); if ((ret.code = new_server_handle(arg->api_version, rqstp, &handle))) return &ret; if ((ret.code = check_handle((void *)handle))) goto error; ret.api_version = handle->api_version; funcname = handle->api_version == KADM5_API_VERSION_1 ? "kadm5_randkey_principal (V1)" : "kadm5_randkey_principal"; if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { ret.code = KADM5_FAILURE; goto error; } if (krb5_unparse_name(handle->context, arg->princ, &prime_arg)) { ret.code = KADM5_BAD_PRINCIPAL; goto error; } if (!(name = get_clnt_name(rqstp))) { ret.code = KADM5_FAILURE; goto error; } if (cmp_gss_krb5_name(handle, name, arg->princ)) { ret.code = randkey_principal_wrapper((void *)handle, arg->princ, &k, &nkeys); } else if (!(CHANGEPW_SERVICE(rqstp)) && kadm5int_acl_check(handle->context, name, ACL_CHANGEPW, arg->princ, NULL)) { ret.code = kadm5_randkey_principal((void *)handle, arg->princ, &k, &nkeys); } else { audit_kadmind_unauth(rqstp->rq_xprt, l_port, funcname, prime_arg, client_name); log_unauth(funcname, prime_arg, client_name, service_name, client_addr(rqstp, buf)); ret.code = KADM5_AUTH_CHANGEPW; } if(ret.code == KADM5_OK) { if (handle->api_version == KADM5_API_VERSION_1) { krb5_copy_keyblock_contents(handle->context, k, &ret.key); krb5_free_keyblock(handle->context, k); } else { ret.keys = k; ret.n_keys = nkeys; } } if(ret.code != KADM5_AUTH_CHANGEPW) { /* Solaris Kerberos */ if( ret.code != 0 ) errmsg = krb5_get_error_message(handle ? handle->context : NULL, ret.code); audit_kadmind_auth(rqstp->rq_xprt, l_port, funcname, prime_arg, client_name, ret.code); log_done(funcname, prime_arg, errmsg ? errmsg : "success", client_name, service_name, client_addr(rqstp, buf)); if (errmsg != NULL) krb5_free_error_message(handle ? handle->context : NULL, errmsg); } error: if (name) gss_release_name(&min_stat, &name); free_server_handle(handle); if (prime_arg) free(prime_arg); if (client_name) free(client_name); if (service_name) free(service_name); return &ret; } chrand_ret * chrand_principal3_2_svc(chrand3_arg *arg, struct svc_req *rqstp) { static chrand_ret ret; krb5_keyblock *k; int nkeys; char *prime_arg = NULL, *funcname; char *client_name = NULL, *service_name = NULL; OM_uint32 min_stat; kadm5_server_handle_t handle; const char *errmsg = NULL; gss_name_t name = NULL; xdr_free(xdr_chrand_ret, (char *) &ret); if ((ret.code = new_server_handle(arg->api_version, rqstp, &handle))) return &ret; if ((ret.code = check_handle((void *)handle))) goto error; ret.api_version = handle->api_version; funcname = handle->api_version == KADM5_API_VERSION_1 ? "kadm5_randkey_principal (V1)" : "kadm5_randkey_principal"; if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { ret.code = KADM5_FAILURE; goto error; } if (krb5_unparse_name(handle->context, arg->princ, &prime_arg)) { ret.code = KADM5_BAD_PRINCIPAL; goto error; } if (!(name = get_clnt_name(rqstp))) { ret.code = KADM5_FAILURE; goto error; } if (cmp_gss_krb5_name(handle, name, arg->princ)) { ret.code = randkey_principal_wrapper_3((void *)handle, arg->princ, arg->keepold, arg->n_ks_tuple, arg->ks_tuple, &k, &nkeys); } else if (!(CHANGEPW_SERVICE(rqstp)) && kadm5int_acl_check(handle->context, name, ACL_CHANGEPW, arg->princ, NULL)) { ret.code = kadm5_randkey_principal_3((void *)handle, arg->princ, arg->keepold, arg->n_ks_tuple, arg->ks_tuple, &k, &nkeys); } else { log_unauth(funcname, prime_arg, client_name, service_name, client_addr(rqstp, buf)); ret.code = KADM5_AUTH_CHANGEPW; } if(ret.code == KADM5_OK) { if (handle->api_version == KADM5_API_VERSION_1) { krb5_copy_keyblock_contents(handle->context, k, &ret.key); krb5_free_keyblock(handle->context, k); } else { ret.keys = k; ret.n_keys = nkeys; } } if(ret.code != KADM5_AUTH_CHANGEPW) { /* Solaris Kerberos */ if( ret.code != 0 ) errmsg = krb5_get_error_message(handle ? handle->context : NULL, ret.code); log_done(funcname, prime_arg, errmsg ? errmsg : "success", client_name, service_name, client_addr(rqstp, buf)); if (errmsg != NULL) krb5_free_error_message(handle ? handle->context : NULL, errmsg); } error: if (name) gss_release_name(&min_stat, &name); free_server_handle(handle); if (client_name) free(client_name); if (service_name) free(service_name); if (prime_arg) free(prime_arg); return (&ret); } generic_ret * create_policy_2_svc(cpol_arg *arg, struct svc_req *rqstp) { static generic_ret ret; char *prime_arg = NULL; char *client_name = NULL, *service_name = NULL; OM_uint32 min_stat; kadm5_server_handle_t handle; const char *errmsg = NULL; gss_name_t name = NULL; xdr_free(xdr_generic_ret, (char *) &ret); if ((ret.code = new_server_handle(arg->api_version, rqstp, &handle))) return &ret; if ((ret.code = check_handle((void *)handle))) goto error; ret.api_version = handle->api_version; if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { ret.code = KADM5_FAILURE; goto error; } prime_arg = arg->rec.policy; if (!(name = get_clnt_name(rqstp))) { ret.code = KADM5_FAILURE; goto error; } if (CHANGEPW_SERVICE(rqstp) || !kadm5int_acl_check(handle->context, name, ACL_ADD, NULL, NULL)) { ret.code = KADM5_AUTH_ADD; audit_kadmind_unauth(rqstp->rq_xprt, l_port, "kadm5_create_policy", prime_arg, client_name); log_unauth("kadm5_create_policy", prime_arg, client_name, service_name, client_addr(rqstp, buf)); } else { ret.code = kadm5_create_policy((void *)handle, &arg->rec, arg->mask); /* Solaris Kerberos */ if( ret.code != 0 ) errmsg = krb5_get_error_message(handle ? handle->context : NULL, ret.code); audit_kadmind_auth(rqstp->rq_xprt, l_port, "kadm5_create_policy", prime_arg, client_name, ret.code); log_done("kadm5_create_policy", ((prime_arg == NULL) ? "(null)" : prime_arg), errmsg ? errmsg : "success", client_name, service_name, client_addr(rqstp, buf)); if (errmsg != NULL) krb5_free_error_message(handle ? handle->context : NULL, errmsg); } error: if (name) gss_release_name(&min_stat, &name); free_server_handle(handle); if (client_name) free(client_name); if (service_name) free(service_name); return &ret; } generic_ret * delete_policy_2_svc(dpol_arg *arg, struct svc_req *rqstp) { static generic_ret ret; char *prime_arg = NULL; char *client_name = NULL, *service_name = NULL; OM_uint32 min_stat; kadm5_server_handle_t handle; const char *errmsg = NULL; gss_name_t name = NULL; xdr_free(xdr_generic_ret, (char *) &ret); if ((ret.code = new_server_handle(arg->api_version, rqstp, &handle))) return &ret; if ((ret.code = check_handle((void *)handle))) goto error; ret.api_version = handle->api_version; if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { ret.code = KADM5_FAILURE; goto error; } prime_arg = arg->name; if (!(name = get_clnt_name(rqstp))) { ret.code = KADM5_FAILURE; goto error; } if (CHANGEPW_SERVICE(rqstp) || !kadm5int_acl_check(handle->context, name, ACL_DELETE, NULL, NULL)) { audit_kadmind_unauth(rqstp->rq_xprt, l_port, "kadm5_delete_policy", prime_arg, client_name); log_unauth("kadm5_delete_policy", prime_arg, client_name, service_name, client_addr(rqstp, buf)); ret.code = KADM5_AUTH_DELETE; } else { ret.code = kadm5_delete_policy((void *)handle, arg->name); /* Solaris Kerberos */ if( ret.code != 0 ) errmsg = krb5_get_error_message(handle ? handle->context : NULL, ret.code); audit_kadmind_auth(rqstp->rq_xprt, l_port, "kadm5_delete_policy", prime_arg, client_name, ret.code); log_done("kadm5_delete_policy", ((prime_arg == NULL) ? "(null)" : prime_arg), errmsg ? errmsg : "success", client_name, service_name, client_addr(rqstp, buf)); if (errmsg != NULL) krb5_free_error_message(handle ? handle->context : NULL, errmsg); } error: if (name) gss_release_name(&min_stat, &name); free_server_handle(handle); if (client_name) free(client_name); if (service_name) free(service_name); return &ret; } generic_ret * modify_policy_2_svc(mpol_arg *arg, struct svc_req *rqstp) { static generic_ret ret; char *prime_arg = NULL; char *client_name = NULL, *service_name = NULL; OM_uint32 min_stat; kadm5_server_handle_t handle; const char *errmsg = NULL; gss_name_t name = NULL; xdr_free(xdr_generic_ret, (char *) &ret); if ((ret.code = new_server_handle(arg->api_version, rqstp, &handle))) return &ret; if ((ret.code = check_handle((void *)handle))) goto error; ret.api_version = handle->api_version; if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { ret.code = KADM5_FAILURE; goto error; } prime_arg = arg->rec.policy; if (!(name = get_clnt_name(rqstp))) { ret.code = KADM5_FAILURE; goto error; } if (CHANGEPW_SERVICE(rqstp) || !kadm5int_acl_check(handle->context, name, ACL_MODIFY, NULL, NULL)) { audit_kadmind_unauth(rqstp->rq_xprt, l_port, "kadm5_modify_policy", prime_arg, client_name); log_unauth("kadm5_modify_policy", prime_arg, client_name, service_name, client_addr(rqstp, buf)); ret.code = KADM5_AUTH_MODIFY; } else { ret.code = kadm5_modify_policy((void *)handle, &arg->rec, arg->mask); /* Solaris Kerberos */ if( ret.code != 0 ) errmsg = krb5_get_error_message(handle ? handle->context : NULL, ret.code); audit_kadmind_auth(rqstp->rq_xprt, l_port, "kadm5_modify_policy", prime_arg, client_name, ret.code); log_done("kadm5_modify_policy", ((prime_arg == NULL) ? "(null)" : prime_arg), errmsg ? errmsg : "success", client_name, service_name, client_addr(rqstp, buf)); if (errmsg != NULL) krb5_free_error_message(handle ? handle->context : NULL, errmsg); } error: if (name) gss_release_name(&min_stat, &name); free_server_handle(handle); if (client_name) free(client_name); if (service_name) free(service_name); return (&ret); } gpol_ret * get_policy_2_svc(gpol_arg *arg, struct svc_req *rqstp) { static gpol_ret ret; kadm5_ret_t ret2; char *prime_arg = NULL, *funcname; char *client_name = NULL, *service_name = NULL; OM_uint32 min_stat; kadm5_policy_ent_t e; kadm5_principal_ent_rec caller_ent; krb5_principal caller; kadm5_server_handle_t handle; const char *errmsg = NULL; gss_name_t name = NULL; xdr_free(xdr_gpol_ret, (char *) &ret); if ((ret.code = new_server_handle(arg->api_version, rqstp, &handle))) return &ret; if ((ret.code = check_handle((void *) handle))) goto error; ret.api_version = handle->api_version; funcname = handle->api_version == KADM5_API_VERSION_1 ? "kadm5_get_policy (V1)" : "kadm5_get_policy"; if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { ret.code = KADM5_FAILURE; goto error; } prime_arg = arg->name; ret.code = KADM5_AUTH_GET; if (!(name = get_clnt_name(rqstp))) { ret.code = KADM5_FAILURE; goto error; } if (!CHANGEPW_SERVICE(rqstp) && kadm5int_acl_check(handle->context, name, ACL_INQUIRE, NULL, NULL)) ret.code = KADM5_OK; else { ret.code = kadm5_get_principal(handle->lhandle, handle->current_caller, &caller_ent, KADM5_PRINCIPAL_NORMAL_MASK); if (ret.code == KADM5_OK) { if (caller_ent.aux_attributes & KADM5_POLICY && strcmp(caller_ent.policy, arg->name) == 0) { ret.code = KADM5_OK; } else ret.code = KADM5_AUTH_GET; ret2 = kadm5_free_principal_ent(handle->lhandle, &caller_ent); ret.code = ret.code ? ret.code : ret2; } } if (ret.code == KADM5_OK) { if (handle->api_version == KADM5_API_VERSION_1) { ret.code = kadm5_get_policy_v1((void *)handle, arg->name, &e); if(ret.code == KADM5_OK) { memcpy(&ret.rec, e, sizeof(kadm5_policy_ent_rec)); free(e); } } else { ret.code = kadm5_get_policy((void *)handle, arg->name, &ret.rec); } /* Solaris Kerberos */ if( ret.code != 0 ) errmsg = krb5_get_error_message(handle ? handle->context : NULL, ret.code); audit_kadmind_auth(rqstp->rq_xprt, l_port, funcname, prime_arg, client_name, ret.code); log_done(funcname, ((prime_arg == NULL) ? "(null)" : prime_arg), errmsg ? errmsg : "success", client_name, service_name, client_addr(rqstp, buf)); if (errmsg != NULL) krb5_free_error_message(handle ? handle->context : NULL, errmsg); } else { audit_kadmind_unauth(rqstp->rq_xprt, l_port, funcname, prime_arg, client_name); log_unauth(funcname, prime_arg, client_name, service_name, client_addr(rqstp, buf)); } error: if (name) gss_release_name(&min_stat, &name); free_server_handle(handle); if (client_name) free(client_name); if (service_name) free(service_name); return (&ret); } gpols_ret * get_pols_2_svc(gpols_arg *arg, struct svc_req *rqstp) { static gpols_ret ret; char *prime_arg = NULL; char *client_name = NULL, *service_name = NULL; OM_uint32 min_stat; kadm5_server_handle_t handle; const char *errmsg = NULL; gss_name_t name = NULL; xdr_free(xdr_gpols_ret, (char *) &ret); if ((ret.code = new_server_handle(arg->api_version, rqstp, &handle))) return &ret; if ((ret.code = check_handle((void *)handle))) goto error; ret.api_version = handle->api_version; if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { ret.code = KADM5_FAILURE; goto error; } prime_arg = arg->exp; if (prime_arg == NULL) prime_arg = "*"; if (!(name = get_clnt_name(rqstp))) { ret.code = KADM5_FAILURE; goto error; } if (CHANGEPW_SERVICE(rqstp) || !kadm5int_acl_check(handle->context, name, ACL_LIST, NULL, NULL)) { ret.code = KADM5_AUTH_LIST; audit_kadmind_unauth(rqstp->rq_xprt, l_port, "kadm5_get_policies", prime_arg, client_name); log_unauth("kadm5_get_policies", prime_arg, client_name, service_name, client_addr(rqstp, buf)); } else { ret.code = kadm5_get_policies((void *)handle, arg->exp, &ret.pols, &ret.count); /* Solaris Kerberos */ if( ret.code != 0 ) errmsg = krb5_get_error_message(handle ? handle->context : NULL, ret.code); audit_kadmind_auth(rqstp->rq_xprt, l_port, "kadm5_get_policies", prime_arg, client_name, ret.code); log_done("kadm5_get_policies", prime_arg, errmsg ? errmsg : "success", client_name, service_name, client_addr(rqstp, buf)); if (errmsg != NULL) krb5_free_error_message(handle ? handle->context : NULL, errmsg); } error: if (name) gss_release_name(&min_stat, &name); free_server_handle(handle); if (client_name) free(client_name); if (service_name) free(service_name); return (&ret); } getprivs_ret * get_privs_2_svc(krb5_ui_4 *arg, struct svc_req *rqstp) { static getprivs_ret ret; char *client_name = NULL, *service_name = NULL; OM_uint32 min_stat; kadm5_server_handle_t handle; const char *errmsg = NULL; gss_name_t name = NULL; xdr_free(xdr_getprivs_ret, (char *) &ret); if ((ret.code = new_server_handle(*arg, rqstp, &handle))) return &ret; if ((ret.code = check_handle((void *)handle))) goto error; ret.api_version = handle->api_version; if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { ret.code = KADM5_FAILURE; goto error; } if (!(name = get_clnt_name(rqstp))) { ret.code = KADM5_FAILURE; goto error; } ret.code = __kadm5_get_priv((void *) handle, &ret.privs, name); /* Solaris Kerberos */ if( ret.code != 0 ) errmsg = krb5_get_error_message(handle ? handle->context : NULL, ret.code); audit_kadmind_auth(rqstp->rq_xprt, l_port, "kadm5_get_privs", NULL, client_name, ret.code); log_done("kadm5_get_privs", client_name, errmsg ? errmsg : "success", client_name, service_name, client_addr(rqstp, buf)); if (errmsg != NULL) krb5_free_error_message(handle ? handle->context : NULL, errmsg); error: if (name) gss_release_name(&min_stat, &name); free_server_handle(handle); if (client_name) free(client_name); if (service_name) free(service_name); return (&ret); } generic_ret *init_2_svc(krb5_ui_4 *arg, struct svc_req *rqstp) { static generic_ret ret; char *client_name, *service_name; kadm5_server_handle_t handle; const char *errmsg = NULL; size_t clen, slen; char *cdots, *sdots; xdr_free(xdr_generic_ret, (char *) &ret); if ((ret.code = new_server_handle(*arg, rqstp, &handle))) return &ret; if (! (ret.code = check_handle((void *)handle))) { ret.api_version = handle->api_version; } free_server_handle(handle); if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { ret.code = KADM5_FAILURE; return &ret; } /* Solaris Kerberos */ if (ret.code != 0) errmsg = krb5_get_error_message(NULL, ret.code); audit_kadmind_auth(rqstp->rq_xprt, l_port, (ret.api_version == KADM5_API_VERSION_1 ? "kadm5_init (V1)" : "kadm5_init"), NULL, client_name, ret.code); clen = strlen(client_name); trunc_name(&clen, &cdots); slen = strlen(service_name); trunc_name(&slen, &sdots); krb5_klog_syslog(LOG_NOTICE, "Request: %s, %.*s%s, %s, " "client=%.*s%s, service=%.*s%s, addr=%s, flavor=%d", (ret.api_version == KADM5_API_VERSION_1 ? "kadm5_init (V1)" : "kadm5_init"), clen, client_name, cdots, errmsg ? errmsg : "success", clen, client_name, cdots, slen, service_name, sdots, client_addr(rqstp, buf), rqstp->rq_cred.oa_flavor); if (errmsg != NULL) krb5_free_error_message(NULL, errmsg); free(client_name); free(service_name); return (&ret); } # # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Copyright (c) 2018, Joyent, Inc. PROG= kdestroy OBJS = kdestroy.o krpc_sys.o SRCS = kdestroy.c krpc_sys.c include ../../Makefile.cmd include $(SRC)/lib/gss_mechs/mech_krb5/Makefile.mech_krb5 CERRWARN += -Wno-implicit-function-declaration CERRWARN += -Wno-parentheses # not linted SMATCH=off POFILE = kdestroy.po POFILES = generic.po CPPFLAGS += -I../../../lib/gss_mechs/mech_krb5/include \ -I$(SRC)/uts/common/gssapi/mechs/krb5/include i386_CPPFLAGS = sparc_CPPFLAGS += -D$(MACH) LDFLAGS += $(KRUNPATH) LDLIBS += $(KMECHLIB) .KEEP_STATE: all: $(PROG) krpc_sys.o: krpc_sys.c $(COMPILE.c) -o $@ krpc_sys.c $(POST_PROCESS_O) $(PROG): $(OBJS) $(LINK.c) $(OBJS) -o $@ $(LDLIBS) $(POST_PROCESS) install: $(KRB5PROG) clean: $(RM) $(OBJS) include ../../Makefile.targ $(POFILE): $(DERIVED_FILES) .WAIT $(POFILES) $(RM) $@ $(CAT) $(POFILES) > $@ generic.po: $(RM) messages.po $(XGETTEXT) $(XGETFLAGS) `($(GREP) -l gettext *.[ch] || echo /dev/null)` $(SED) "/^domain/d" messages.po > $@ $(RM) messages.po /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * clients/kdestroy/kdestroy.c * * Copyright 1990 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. * * * Destroy the contents of your credential cache. */ #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif #include #include #include #include #include #include #ifdef KRB5_KRB4_COMPAT #include #endif #ifdef __STDC__ #define BELL_CHAR '\a' #else #define BELL_CHAR '\007' #endif extern int optind; extern char *optarg; #ifndef _WIN32 #define GET_PROGNAME(x) (strrchr((x), '/') ? strrchr((x), '/')+1 : (x)) #else #define GET_PROGNAME(x) max(max(strrchr((x), '/'), strrchr((x), '\\')) + 1,(x)) #endif char *progname; int got_k5 = 0; int got_k4 = 0; int default_k5 = 1; #ifdef KRB5_KRB4_COMPAT int default_k4 = 1; #else int default_k4 = 0; #endif static void usage() { #define KRB_AVAIL_STRING(x) ((x)?gettext("available"):gettext("not available")) fprintf(stderr, gettext("Usage"), ": %s [-5] [-4] [-q] [-c cache_name]\n", progname); fprintf(stderr, "\t-5 Kerberos 5 (%s)\n", KRB_AVAIL_STRING(got_k5)); fprintf(stderr, "\t-4 Kerberos 4 (%s)\n", KRB_AVAIL_STRING(got_k4)); fprintf(stderr, gettext("\t (Default is %s%s%s%s)\n"), default_k5?"Kerberos 5":"", (default_k5 && default_k4)?gettext(" and "):"", default_k4?"Kerberos 4":"", (!default_k5 && !default_k4)?gettext("neither"):""); fprintf(stderr, gettext("\t-q quiet mode\n")); fprintf(stderr, gettext("\t-c specify name of credentials cache\n")); exit(2); } int main(argc, argv) int argc; char **argv; { krb5_context kcontext; krb5_error_code retval; int c; krb5_ccache cache = NULL; char *cache_name = NULL; char *client_name = NULL; krb5_principal me; int code = 0; #ifdef KRB5_KRB4_COMPAT int v4code = 0; int v4 = 1; #endif int errflg = 0; int quiet = 0; struct krpc_revauth desarg; static rpc_gss_OID_desc oid= {9, "\052\206\110\206\367\022\001\002\002"}; static rpc_gss_OID krb5_mech_type = &oid; int use_k5 = 0; int use_k4 = 0; progname = GET_PROGNAME(argv[0]); /* set locale and domain for internationalization */ (void) setlocale(LC_ALL, ""); #if !defined(TEXT_DOMAIN) #define TEXT_DOMAIN "SYS_TEST" #endif /* !TEXT_DOMAIN */ (void) textdomain(TEXT_DOMAIN); got_k5 = 1; #ifdef KRB5_KRB4_COMPAT got_k4 = 1; #endif while ((c = getopt(argc, argv, "54qc:")) != -1) { switch (c) { case 'q': quiet = 1; break; case 'c': if (cache_name) { fprintf(stderr, gettext("Only one -c option allowed\n")); errflg++; } else { cache_name = optarg; } break; case '4': if (!got_k4) { #ifdef KRB5_KRB4_COMPAT fprintf(stderr, "Kerberos 4 support could not be loaded\n"); #else fprintf(stderr, gettext("This was not built with Kerberos 4 support\n")); #endif exit(3); } use_k4 = 1; break; case '5': if (!got_k5) { fprintf(stderr, gettext("Kerberos 5 support could not be loaded\n")); exit(3); } use_k5 = 1; break; case '?': default: errflg++; break; } } if (optind != argc) errflg++; if (errflg) { usage(); } if (!use_k5 && !use_k4) { use_k5 = default_k5; use_k4 = default_k4; } if (!use_k5) got_k5 = 0; if (!use_k4) got_k4 = 0; if (got_k5) { retval = krb5_init_context(&kcontext); if (retval) { com_err(progname, retval, gettext("while initializing krb5")); exit(1); } /* * Solaris Kerberos * Let us destroy the kernel cache first */ desarg.version = 1; desarg.uid_1 = geteuid(); desarg.rpcsec_flavor_1 = RPCSEC_GSS; desarg.flavor_data_1 = (void *) krb5_mech_type; code = krpc_sys(KRPC_REVAUTH, (void *)&desarg); if (code != 0) { fprintf(stderr, gettext("%s: kernel creds cache error %d \n"), progname, code); } if (cache == NULL) { if (code = krb5_cc_default(kcontext, &cache)) { com_err(progname, code, gettext("while getting default ccache")); exit(1); } } if (cache_name) { #ifdef KRB5_KRB4_COMPAT v4 = 0; /* Don't do v4 if doing v5 and cache name given. */ #endif code = krb5_cc_resolve (kcontext, cache_name, &cache); if (code != 0) { com_err (progname, code, gettext("while resolving %s"), cache_name); exit(1); } } else { code = krb5_cc_default(kcontext, &cache); if (code) { com_err(progname, code, gettext("while getting default ccache")); exit(1); } } /* * Solaris Kerberos * Get client name for kwarn_del_warning. */ code = krb5_cc_get_principal(kcontext, cache, &me); if (code != 0) fprintf(stderr, gettext ("%s: Could not obtain principal name from cache\n"), progname); else if ((code = krb5_unparse_name(kcontext, me, &client_name))) fprintf(stderr, gettext ("%s: Could not unparse principal name found in cache\n"), progname); code = krb5_cc_destroy (kcontext, cache); if (code != 0) { com_err (progname, code, gettext("while destroying cache")); if (code != KRB5_FCC_NOFILE) { if (quiet) fprintf(stderr, gettext("Ticket cache NOT destroyed!\n")); else { fprintf(stderr, gettext("Ticket cache %cNOT%c destroyed!\n"), BELL_CHAR, BELL_CHAR); } errflg = 1; } } } #ifdef KRB5_KRB4_COMPAT if (got_k4 && v4) { v4code = dest_tkt(); if (v4code == KSUCCESS && code != 0) fprintf(stderr, "Kerberos 4 ticket cache destroyed.\n"); if (v4code != KSUCCESS && v4code != RET_TKFIL) { if (quiet) fprintf(stderr, "Kerberos 4 ticket cache NOT destroyed!\n"); else fprintf(stderr, "Kerberos 4 ticket cache %cNOT%c destroyed!\n", BELL_CHAR, BELL_CHAR); errflg = 1; } } #endif /* Solaris Kerberos */ if (!errflg && client_name) kwarn_del_warning(client_name); else fprintf(stderr, gettext ("%s: TGT expire warning NOT deleted\n"), progname); return errflg; } /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include int krpc_sys(int cmd, void *args) { return (_rpcsys(cmd, args)); } # # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Copyright (c) 2018, Joyent, Inc. PROG= kinit OBJS = kinit.o SRCS = kinit.c DEFS = -DHAVE_LIBSOCKET=1 -DHAVE_LIBNSL=1 -DHAVE_PWD_H=1 -DHAVE_COMPILE=1 -DHAVE_STEP=1 include ../../Makefile.cmd include $(SRC)/lib/gss_mechs/mech_krb5/Makefile.mech_krb5 CERRWARN += -Wno-implicit-function-declaration CERRWARN += -Wno-unused-function CERRWARN += -Wno-parentheses # not linted SMATCH=off POFILE = kinit.po POFILES = generic.po DEFS = -DHAVE_PWD_H CPPFLAGS += -I../../lib/gss_mechs/mech_krb5/include \ -I$(SRC)/lib/gss_mechs/mech_krb5 \ -I$(SRC)/lib/gss_mechs/mech_krb5/include \ -I$(SRC)/uts/common/gssapi/mechs/krb5/include \ $(DEFS) LDFLAGS += $(KRUNPATH) LDLIBS += $(KMECHLIB) .KEEP_STATE: all: $(PROG) $(PROG): $(OBJS) $(LINK.c) $(OBJS) -o $@ $(LDLIBS) $(POST_PROCESS) install: $(KRB5PROG) clean: $(RM) $(OBJS) include ../../Makefile.targ $(POFILE): $(DERIVED_FILES) .WAIT $(POFILES) $(RM) $@ $(CAT) $(POFILES) > $@ generic.po: $(RM) messages.po $(XGETTEXT) $(XGETFLAGS) `($(GREP) -l gettext *.[ch] || echo /dev/null)` $(SED) "/^domain/d" messages.po > $@ $(RM) messages.po /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * clients/kinit/kinit.c * * Copyright 1990 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. * * * Initialize a credentials cache. */ #include #include #include #include #include #ifdef KRB5_KRB4_COMPAT #include #define HAVE_KRB524 #else #undef HAVE_KRB524 #endif #include #include #include #include #include #include #include #ifdef GETOPT_LONG #include #else #ifdef HAVE_UNISTD_H #include #ifdef sun /* SunOS4 unistd didn't declare these; okay to make unconditional? */ extern int optind; extern char *optarg; #endif /* sun */ #else extern int optind; extern char *optarg; extern int getopt(); #endif /* HAVE_UNISTD_H */ #endif /* GETOPT_LONG */ #ifndef _WIN32 #define GET_PROGNAME(x) (strrchr((x), '/') ? strrchr((x), '/')+1 : (x)) #else #define GET_PROGNAME(x) max(max(strrchr((x), '/'), strrchr((x), '\\')) + 1,(x)) #endif #ifdef HAVE_PWD_H #include static char * get_name_from_os() { struct passwd *pw; if ((pw = getpwuid((int) getuid()))) return pw->pw_name; return 0; } #else /* HAVE_PWD_H */ #ifdef _WIN32 static char * get_name_from_os() { static char name[1024]; DWORD name_size = sizeof(name); if (GetUserName(name, &name_size)) { name[sizeof(name)-1] = 0; /* Just to be extra safe */ return name; } else { return 0; } } #else /* _WIN32 */ static char * get_name_from_os() { return 0; } #endif /* _WIN32 */ #endif /* HAVE_PWD_H */ static char* progname_v5 = 0; #ifdef KRB5_KRB4_COMPAT static char* progname_v4 = 0; static char* progname_v524 = 0; #endif #include static int got_k5 = 0; static int got_k4 = 0; static int default_k5 = 1; #if defined(KRB5_KRB4_COMPAT) && defined(KINIT_DEFAULT_BOTH) static int default_k4 = 1; #else static int default_k4 = 0; #endif static int authed_k5 = 0; static int authed_k4 = 0; #define KRB4_BACKUP_DEFAULT_LIFE_SECS 24*60*60 /* 1 day */ #define ROOT_UNAME "root" typedef enum { INIT_PW, INIT_KT, RENEW, VALIDATE } action_type; struct k_opts { /* in seconds */ krb5_deltat starttime; krb5_deltat lifetime; krb5_deltat rlife; int forwardable; int proxiable; int addresses; int not_forwardable; int not_proxiable; int no_addresses; int verbose; char* principal_name; char* service_name; char* keytab_name; char* k5_cache_name; char* k4_cache_name; action_type action; int num_pa_opts; krb5_gic_opt_pa_data *pa_opts; }; int forwardable_flag = 0; int renewable_flag = 0; int proxiable_flag = 0; int no_address_flag = 0; profile_options_boolean config_option[] = { { "forwardable", &forwardable_flag, 0 }, { "renewable", &renewable_flag, 0 }, { "proxiable", &proxiable_flag, 0 }, { "no_addresses", &no_address_flag, 0 }, { NULL, NULL, 0 } }; char *renew_timeval=NULL; char *life_timeval=NULL; int lifetime_specified; int renewtime_specified; profile_option_strings config_times[] = { { "max_life", &life_timeval, 0 }, { "max_renewable_life", &renew_timeval, 0 }, { NULL, NULL, 0 } }; struct k5_data { krb5_context ctx; krb5_ccache cc; krb5_principal me; char* name; }; struct k4_data { krb5_deltat lifetime; #ifdef KRB5_KRB4_COMPAT char aname[ANAME_SZ + 1]; char inst[INST_SZ + 1]; char realm[REALM_SZ + 1]; char name[ANAME_SZ + 1 + INST_SZ + 1 + REALM_SZ + 1]; #endif }; char *realmdef[] = { "realms", NULL, "kinit", NULL }; char *appdef[] = { "appdefaults", "kinit", NULL }; #define krb_realm (*(realmdef + 1)) #define lifetime_specified config_times[0].found #define renewtime_specified config_times[1].found /* * Try no preauthentication first; then try the encrypted timestamp */ krb5_preauthtype * preauth = NULL; krb5_preauthtype preauth_list[2] = { 0, -1 }; static void _kwarnd_add_warning(char *, char *, time_t); static void _kwarnd_del_warning(char *, char *); #ifdef GETOPT_LONG /* if struct[2] == NULL, then long_getopt acts as if the short flag struct[3] was specified. If struct[2] != NULL, then struct[3] is stored in *(struct[2]), the array index which was specified is stored in *index, and long_getopt() returns 0. */ struct option long_options[] = { { "noforwardable", 0, NULL, 'F' }, { "noproxiable", 0, NULL, 'P' }, { "addresses", 0, NULL, 'a'}, { "forwardable", 0, NULL, 'f' }, { "proxiable", 0, NULL, 'p' }, { "noaddresses", 0, NULL, 'A' }, { NULL, 0, NULL, 0 } }; #define GETOPT(argc, argv, str) getopt_long(argc, argv, str, long_options, 0) #else #define GETOPT(argc, argv, str) getopt(argc, argv, str) #endif static void usage(progname) char *progname; { #define USAGE_BREAK "\n\t" #ifdef GETOPT_LONG #define USAGE_LONG_FORWARDABLE " | --forwardable | --noforwardable" #define USAGE_LONG_PROXIABLE " | --proxiable | --noproxiable" #define USAGE_LONG_ADDRESSES " | --addresses | --noaddresses" #define USAGE_BREAK_LONG USAGE_BREAK #else #define USAGE_LONG_FORWARDABLE "" #define USAGE_LONG_PROXIABLE "" #define USAGE_LONG_ADDRESSES "" #define USAGE_BREAK_LONG "" #endif fprintf(stderr, "%s : %s [-V] " "[-l lifetime] [-s start_time] " USAGE_BREAK "[-r renewable_life] " "[-f | -F" USAGE_LONG_FORWARDABLE "] " USAGE_BREAK_LONG "[-p | -P" USAGE_LONG_PROXIABLE "] " USAGE_BREAK_LONG "[-a | -A" USAGE_LONG_ADDRESSES "] " USAGE_BREAK "[-v] [-R] " "[-k [-t keytab_file]] " "[-c cachename] " USAGE_BREAK "[-S service_name]" "[-X [=]] [principal]" "\n\n", gettext("Usage"), progname); #define KRB_AVAIL_STRING(x) ((x)?gettext("available"):gettext("not available")) #define OPTTYPE_KRB5 "5" #define OPTTYPE_KRB4 "4" #define OPTTYPE_EITHER "Either 4 or 5" #ifdef HAVE_KRB524 #define OPTTYPE_BOTH "5, or both 5 and 4" #else #define OPTTYPE_BOTH "5" #endif #ifdef KRB5_KRB4_COMPAT #define USAGE_OPT_FMT "%s%-50s%s\n" #define ULINE(indent, col1, col2) \ fprintf(stderr, USAGE_OPT_FMT, indent, col1, col2) #else #define USAGE_OPT_FMT "%s%s\n" #define ULINE(indent, col1, col2) \ fprintf(stderr, USAGE_OPT_FMT, indent, col1) #endif ULINE(" ", "options:", "valid with Kerberos:"); fprintf(stderr, "\t-5 Kerberos 5 (%s)\n", KRB_AVAIL_STRING(got_k5)); fprintf(stderr, "\t-4 Kerberos 4 (%s)\n", KRB_AVAIL_STRING(got_k4)); fprintf(stderr, "\t (Default behavior is to try %s%s%s%s)\n", default_k5?"Kerberos 5":"", (default_k5 && default_k4)?gettext(" and "):"", default_k4?"Kerberos 4":"", (!default_k5 && !default_k4)?gettext("neither"):""); ULINE("\t", gettext("-V verbose"), OPTTYPE_EITHER); ULINE("\t", gettext("-l lifetime"), OPTTYPE_EITHER); ULINE("\t", gettext("-s start time"), OPTTYPE_KRB5); ULINE("\t", gettext("-r renewable lifetime"), OPTTYPE_KRB5); ULINE("\t", gettext("-f forwardable"), OPTTYPE_KRB5); ULINE("\t", gettext("-F not forwardable"), OPTTYPE_KRB5); ULINE("\t", gettext("-p proxiable"), OPTTYPE_KRB5); ULINE("\t", gettext("-P not proxiable"), OPTTYPE_KRB5); ULINE("\t", gettext("-A do not include addresses"), OPTTYPE_KRB5); ULINE("\t", gettext("-a include addresses"), OPTTYPE_KRB5); ULINE("\t", gettext("-v validate"), OPTTYPE_KRB5); ULINE("\t", gettext("-R renew"), OPTTYPE_BOTH); ULINE("\t", gettext("-k use keytab"), OPTTYPE_BOTH); ULINE("\t", gettext("-t filename of keytab to use"), OPTTYPE_BOTH); ULINE("\t", gettext("-c Kerberos 5 cache name"), OPTTYPE_KRB5); /* This options is not yet available: */ /* ULINE("\t", "-C Kerberos 4 cache name", OPTTYPE_KRB4); */ ULINE("\t", gettext("-S service"), OPTTYPE_BOTH); ULINE("\t", gettext("-X [=]"), OPTTYPE_KRB5); exit(2); } static krb5_context errctx; static void extended_com_err_fn (const char *myprog, errcode_t code, const char *fmt, va_list args) { const char *emsg; emsg = krb5_get_error_message (errctx, code); fprintf (stderr, "%s: %s ", myprog, emsg); krb5_free_error_message (errctx, emsg); vfprintf (stderr, fmt, args); fprintf (stderr, "\n"); } static int add_preauth_opt(struct k_opts *opts, char *av) { char *sep, *v; krb5_gic_opt_pa_data *p, *x; if (opts->num_pa_opts == 0) { opts->pa_opts = malloc(sizeof(krb5_gic_opt_pa_data)); if (opts->pa_opts == NULL) return ENOMEM; } else { size_t newsize = (opts->num_pa_opts + 1) * sizeof(krb5_gic_opt_pa_data); x = realloc(opts->pa_opts, newsize); if (x == NULL) return ENOMEM; opts->pa_opts = x; } p = &opts->pa_opts[opts->num_pa_opts]; sep = strchr(av, '='); if (sep) { *sep = '\0'; v = ++sep; p->value = v; } else { p->value = "yes"; } p->attr = av; opts->num_pa_opts++; return 0; } static char * parse_options(argc, argv, opts, progname) int argc; char **argv; struct k_opts* opts; char *progname; { krb5_error_code code; int errflg = 0; int use_k4 = 0; int use_k5 = 0; int i; while ((i = GETOPT(argc, argv, "r:fpFP54aAVl:s:c:kt:RS:vX:")) != -1) { switch (i) { case 'V': opts->verbose = 1; break; case 'l': /* Lifetime */ code = krb5_string_to_deltat(optarg, &opts->lifetime); if (code != 0 || opts->lifetime == 0) { fprintf(stderr, gettext("Bad lifetime value %s\n"), optarg); errflg++; } break; case 'r': /* Renewable Time */ code = krb5_string_to_deltat(optarg, &opts->rlife); if (code != 0 || opts->rlife == 0) { fprintf(stderr, gettext("Bad lifetime value %s\n"), optarg); errflg++; } break; case 'f': opts->forwardable = 1; break; case 'F': opts->not_forwardable = 1; break; case 'p': opts->proxiable = 1; break; case 'P': opts->not_proxiable = 1; break; case 'a': /* Note: This is supported only with GETOPT_LONG */ opts->addresses = 1; break; case 'A': opts->no_addresses = 1; break; case 's': code = krb5_string_to_deltat(optarg, &opts->starttime); if (code != 0 || opts->starttime == 0) { krb5_timestamp abs_starttime; code = krb5_string_to_timestamp(optarg, &abs_starttime); if (code != 0 || abs_starttime == 0) { fprintf(stderr, gettext("Bad start time value %s\n"), optarg); errflg++; } else { opts->starttime = abs_starttime - time(0); } } break; case 'S': opts->service_name = optarg; break; case 'k': opts->action = INIT_KT; break; case 't': if (opts->keytab_name) { fprintf(stderr, gettext("Only one -t option allowed.\n")); errflg++; } else { opts->keytab_name = optarg; } break; case 'R': opts->action = RENEW; break; case 'v': opts->action = VALIDATE; break; case 'c': if (opts->k5_cache_name) { fprintf(stderr, gettext("Only one -c option allowed\n")); errflg++; } else { opts->k5_cache_name = optarg; } break; case 'X': code = add_preauth_opt(opts, optarg); if (code) { com_err(progname, code, "while adding preauth option"); errflg++; } break; #if 0 /* A little more work is needed before we can enable this option. */ case 'C': if (opts->k4_cache_name) { fprintf(stderr, "Only one -C option allowed\n"); errflg++; } else { opts->k4_cache_name = optarg; } break; #endif case '4': if (!got_k4) { #ifdef KRB5_KRB4_COMPAT fprintf(stderr, "Kerberos 4 support could not be loaded\n"); #else fprintf(stderr, gettext("This was not built with Kerberos 4 support\n")); #endif exit(3); } use_k4 = 1; break; case '5': if (!got_k5) { fprintf(stderr, gettext("Kerberos 5 support could not be loaded\n")); exit(3); } use_k5 = 1; break; default: errflg++; break; } } if (opts->forwardable && opts->not_forwardable) { fprintf(stderr, gettext("Only one of -f and -F allowed\n")); errflg++; } if (opts->proxiable && opts->not_proxiable) { fprintf(stderr, gettext("Only one of -p and -P allowed\n")); errflg++; } if (opts->addresses && opts->no_addresses) { fprintf(stderr, gettext("Only one of -a and -A allowed\n")); errflg++; } if (argc - optind > 1) { fprintf(stderr, gettext("Extra arguments (starting with \"%s\").\n"), argv[optind+1]); errflg++; } /* At this point, if errorless, we know we only have one option selection */ if (!use_k5 && !use_k4) { use_k5 = default_k5; use_k4 = default_k4; } /* Now, we encode the OPTTYPE stuff here... */ if (!use_k5 && (opts->starttime || opts->rlife || opts->forwardable || opts->proxiable || opts->addresses || opts->not_forwardable || opts->not_proxiable || opts->no_addresses || (opts->action == VALIDATE) || opts->k5_cache_name)) { fprintf(stderr, gettext("Specified option that requires Kerberos 5\n")); errflg++; } if (!use_k4 && opts->k4_cache_name) { fprintf(stderr, gettext("Specified option that require Kerberos 4\n")); errflg++; } if ( #ifdef HAVE_KRB524 !use_k5 #else use_k4 #endif && (opts->service_name || opts->keytab_name || (opts->action == INIT_KT) || (opts->action == RENEW)) ) { fprintf(stderr, gettext("Specified option that requires Kerberos 5\n")); errflg++; } if (errflg) { usage(progname); } got_k5 = got_k5 && use_k5; got_k4 = got_k4 && use_k4; opts->principal_name = (optind == argc-1) ? argv[optind] : 0; return opts->principal_name; } static int k5_begin(opts, k5, k4) struct k_opts* opts; struct k5_data* k5; struct k4_data* k4; { char* progname = progname_v5; krb5_error_code code = 0; if (!got_k5) return 0; code = krb5_init_context(&k5->ctx); if (code) { com_err(progname, code, gettext("while initializing Kerberos 5 library")); return 0; } errctx = k5->ctx; if (opts->k5_cache_name) { code = krb5_cc_resolve(k5->ctx, opts->k5_cache_name, &k5->cc); if (code != 0) { com_err(progname, code, gettext("resolving ccache %s"), opts->k5_cache_name); return 0; } } else { if ((code = krb5_cc_default(k5->ctx, &k5->cc))) { com_err(progname, code, gettext("while getting default ccache")); return 0; } } if (opts->principal_name) { /* Use specified name */ if ((code = krb5_parse_name(k5->ctx, opts->principal_name, &k5->me))) { com_err(progname, code, gettext("when parsing name %s"), opts->principal_name); return 0; } } else { /* No principal name specified */ if (opts->action == INIT_KT) { /* Use the default host/service name */ code = krb5_sname_to_principal(k5->ctx, NULL, NULL, KRB5_NT_SRV_HST, &k5->me); if (code) { com_err(progname, code, gettext( "when creating default server principal name")); return 0; } } else { /* Get default principal from cache if one exists */ code = krb5_cc_get_principal(k5->ctx, k5->cc, &k5->me); if (code) { char *name = get_name_from_os(); if (!name) { fprintf(stderr, gettext("Unable to identify user\n")); return 0; } /* use strcmp to ensure only "root" is matched */ if (strcmp(name, ROOT_UNAME) == 0) { if (code = krb5_sname_to_principal(k5->ctx, NULL, ROOT_UNAME, KRB5_NT_SRV_HST, &k5->me)) { com_err(progname, code, gettext( "when creating default server principal name")); return 0; } } else if ((code = krb5_parse_name(k5->ctx, name, &k5->me))) { com_err(progname, code, gettext("when parsing name %s"), name); return 0; } } } } code = krb5_unparse_name(k5->ctx, k5->me, &k5->name); if (code) { com_err(progname, code, gettext("when unparsing name")); return 0; } opts->principal_name = k5->name; #ifdef KRB5_KRB4_COMPAT if (got_k4) { /* Translate to a Kerberos 4 principal */ code = krb5_524_conv_principal(k5->ctx, k5->me, k4->aname, k4->inst, k4->realm); if (code) { k4->aname[0] = 0; k4->inst[0] = 0; k4->realm[0] = 0; } } #endif return 1; } static void k5_end(k5) struct k5_data* k5; { if (k5->name) krb5_free_unparsed_name(k5->ctx, k5->name); if (k5->me) krb5_free_principal(k5->ctx, k5->me); if (k5->cc) krb5_cc_close(k5->ctx, k5->cc); if (k5->ctx) krb5_free_context(k5->ctx); errctx = NULL; memset(k5, 0, sizeof(*k5)); } static int k4_begin(opts, k4) struct k_opts* opts; struct k4_data* k4; { #ifdef KRB5_KRB4_COMPAT char* progname = progname_v4; int k_errno = 0; #endif if (!got_k4) return 0; #ifdef KRB5_KRB4_COMPAT if (k4->aname[0]) goto skip; if (opts->principal_name) { /* Use specified name */ k_errno = kname_parse(k4->aname, k4->inst, k4->realm, opts->principal_name); if (k_errno) { fprintf(stderr, "%s: %s\n", progname, krb_get_err_text(k_errno)); return 0; } } else { /* No principal name specified */ if (opts->action == INIT_KT) { /* Use the default host/service name */ /* XXX - need to add this functionality */ fprintf(stderr, "%s: Kerberos 4 srvtab support is not " "implemented\n", progname); return 0; } else { /* Get default principal from cache if one exists */ k_errno = krb_get_tf_fullname(tkt_string(), k4->aname, k4->inst, k4->realm); if (k_errno) { char *name = get_name_from_os(); if (!name) { fprintf(stderr, "Unable to identify user\n"); return 0; } k_errno = kname_parse(k4->aname, k4->inst, k4->realm, name); if (k_errno) { fprintf(stderr, "%s: %s\n", progname, krb_get_err_text(k_errno)); return 0; } } } } if (!k4->realm[0]) krb_get_lrealm(k4->realm, 1); if (k4->inst[0]) sprintf(k4->name, "%s.%s@%s", k4->aname, k4->inst, k4->realm); else sprintf(k4->name, "%s@%s", k4->aname, k4->realm); opts->principal_name = k4->name; skip: if (k4->aname[0] && !k_isname(k4->aname)) { fprintf(stderr, "%s: bad Kerberos 4 name format\n", progname); return 0; } if (k4->inst[0] && !k_isinst(k4->inst)) { fprintf(stderr, "%s: bad Kerberos 4 instance format\n", progname); return 0; } if (k4->realm[0] && !k_isrealm(k4->realm)) { fprintf(stderr, "%s: bad Kerberos 4 realm format\n", progname); return 0; } #endif /* KRB5_KRB4_COMPAT */ return 1; } static void k4_end(k4) struct k4_data* k4; { memset(k4, 0, sizeof(*k4)); } #ifdef KRB5_KRB4_COMPAT static char stash_password[1024]; static int got_password = 0; #endif /* KRB5_KRB4_COMPAT */ static krb5_error_code KRB5_CALLCONV kinit_prompter( krb5_context ctx, void *data, const char *name, const char *banner, int num_prompts, krb5_prompt prompts[] ) { int i; krb5_prompt_type *types; krb5_error_code rc = krb5_prompter_posix(ctx, data, name, banner, num_prompts, prompts); if (!rc && (types = krb5_get_prompt_types(ctx))) for (i = 0; i < num_prompts; i++) if ((types[i] == KRB5_PROMPT_TYPE_PASSWORD) || (types[i] == KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN)) { #ifdef KRB5_KRB4_COMPAT strncpy(stash_password, prompts[i].reply->data, sizeof(stash_password)); got_password = 1; #endif } return rc; } static int k5_kinit(opts, k5) struct k_opts* opts; struct k5_data* k5; { char* progname = progname_v5; int notix = 1; krb5_keytab keytab = 0; krb5_creds my_creds; krb5_error_code code = 0; krb5_get_init_creds_opt *options = NULL; int i; krb5_timestamp now; krb5_deltat lifetime = 0, rlife = 0, krb5_max_duration; if (!got_k5) return 0; code = krb5_get_init_creds_opt_alloc(k5->ctx, &options); if (code) goto cleanup; memset(&my_creds, 0, sizeof(my_creds)); /* * Solaris Kerberos: added support for max_life and max_renewable_life * which should be removed in the next minor release. See PSARC 2003/545 * for more info. * * Also, check krb5.conf for proxiable/forwardable/renewable/no_address * parameter values. */ /* If either tkt life or renew life weren't set earlier take common steps to * get the krb5.conf parameter values. */ if ((code = krb5_timeofday(k5->ctx, &now))) { com_err(progname, code, gettext("while getting time of day")); exit(1); } krb5_max_duration = KRB5_KDB_EXPIRATION - now - 60*60; if (opts->lifetime == 0 || opts->rlife == 0) { krb_realm = krb5_princ_realm(k5->ctx, k5->me)->data; /* realm params take precedence */ profile_get_options_string(k5->ctx->profile, realmdef, config_times); profile_get_options_string(k5->ctx->profile, appdef, config_times); /* if the input opts doesn't have lifetime set and the krb5.conf * parameter has been set, use that. */ if (opts->lifetime == 0 && life_timeval != NULL) { code = krb5_string_to_deltat(life_timeval, &lifetime); if (code != 0 || lifetime == 0 || lifetime > krb5_max_duration) { fprintf(stderr, gettext("Bad max_life " "value in Kerberos config file %s\n"), life_timeval); exit(1); } opts->lifetime = lifetime; } if (opts->rlife == 0 && renew_timeval != NULL) { code = krb5_string_to_deltat(renew_timeval, &rlife); if (code != 0 || rlife == 0 || rlife > krb5_max_duration) { fprintf(stderr, gettext("Bad max_renewable_life " "value in Kerberos config file %s\n"), renew_timeval); exit(1); } opts->rlife = rlife; } } /* * If lifetime is not set on the cmdline or in the krb5.conf * file, default to max. */ if (opts->lifetime == 0) opts->lifetime = krb5_max_duration; profile_get_options_boolean(k5->ctx->profile, realmdef, config_option); profile_get_options_boolean(k5->ctx->profile, appdef, config_option); /* cmdline opts take precedence over krb5.conf file values */ if (!opts->not_proxiable && proxiable_flag) { krb5_get_init_creds_opt_set_proxiable(options, 1); } if (!opts->not_forwardable && forwardable_flag) { krb5_get_init_creds_opt_set_forwardable(options, 1); } if (renewable_flag) { /* * If this flag is set in krb5.conf, but rlife is 0, then * set it to the max (and let the KDC sort it out). */ opts->rlife = opts->rlife ? opts->rlife : krb5_max_duration; } if (no_address_flag) { /* cmdline opts will overwrite this below if needbe */ krb5_get_init_creds_opt_set_address_list(options, NULL); } /* From this point on, we can goto cleanup because my_creds is initialized. */ if (opts->lifetime) krb5_get_init_creds_opt_set_tkt_life(options, opts->lifetime); if (opts->rlife) krb5_get_init_creds_opt_set_renew_life(options, opts->rlife); if (opts->forwardable) krb5_get_init_creds_opt_set_forwardable(options, 1); if (opts->not_forwardable) krb5_get_init_creds_opt_set_forwardable(options, 0); if (opts->proxiable) krb5_get_init_creds_opt_set_proxiable(options, 1); if (opts->not_proxiable) krb5_get_init_creds_opt_set_proxiable(options, 0); if (opts->addresses) { krb5_address **addresses = NULL; code = krb5_os_localaddr(k5->ctx, &addresses); if (code != 0) { com_err(progname, code, gettext("getting local addresses")); goto cleanup; } krb5_get_init_creds_opt_set_address_list(options, addresses); } if (opts->no_addresses) krb5_get_init_creds_opt_set_address_list(options, NULL); if ((opts->action == INIT_KT) && opts->keytab_name) { code = krb5_kt_resolve(k5->ctx, opts->keytab_name, &keytab); if (code != 0) { com_err(progname, code, gettext("resolving keytab %s"), opts->keytab_name); goto cleanup; } } for (i = 0; i < opts->num_pa_opts; i++) { code = krb5_get_init_creds_opt_set_pa(k5->ctx, options, opts->pa_opts[i].attr, opts->pa_opts[i].value); if (code != 0) { com_err(progname, code, "while setting '%s'='%s'", opts->pa_opts[i].attr, opts->pa_opts[i].value); goto cleanup; } } switch (opts->action) { case INIT_PW: code = krb5_get_init_creds_password(k5->ctx, &my_creds, k5->me, 0, kinit_prompter, 0, opts->starttime, opts->service_name, options); break; case INIT_KT: code = krb5_get_init_creds_keytab(k5->ctx, &my_creds, k5->me, keytab, opts->starttime, opts->service_name, options); break; case VALIDATE: code = krb5_get_validated_creds(k5->ctx, &my_creds, k5->me, k5->cc, opts->service_name); break; case RENEW: code = krb5_get_renewed_creds(k5->ctx, &my_creds, k5->me, k5->cc, opts->service_name); break; } if (code) { char *doing = 0; switch (opts->action) { case INIT_PW: case INIT_KT: doing = gettext("getting initial credentials"); break; case VALIDATE: doing = gettext("validating credentials"); break; case RENEW: doing = gettext("renewing credentials"); break; } /* If got code == KRB5_AP_ERR_V4_REPLY && got_k4, we should let the user know that maybe he/she wants -4. */ if (code == KRB5KRB_AP_ERR_V4_REPLY && got_k4) com_err(progname, code, "while %s\n" "The KDC doesn't support v5. " "You may want the -4 option in the future", doing); else if (code == KRB5KRB_AP_ERR_BAD_INTEGRITY) fprintf(stderr, gettext("%s: Password incorrect while %s\n"), progname, doing); else com_err(progname, code, gettext("while %s"), doing); goto cleanup; } if (!opts->lifetime) { /* We need to figure out what lifetime to use for Kerberos 4. */ opts->lifetime = my_creds.times.endtime - my_creds.times.authtime; } code = krb5_cc_initialize(k5->ctx, k5->cc, k5->me); if (code) { com_err(progname, code, gettext("when initializing cache %s"), opts->k5_cache_name?opts->k5_cache_name:""); goto cleanup; } code = krb5_cc_store_cred(k5->ctx, k5->cc, &my_creds); if (code) { com_err(progname, code, gettext("while storing credentials")); goto cleanup; } if (opts->action == RENEW) { _kwarnd_del_warning(progname, opts->principal_name); _kwarnd_add_warning(progname, opts->principal_name, my_creds.times.endtime); } else if ((opts->action == INIT_KT) || (opts->action == INIT_PW)) { _kwarnd_add_warning(progname, opts->principal_name, my_creds.times.endtime); } notix = 0; cleanup: if (options) krb5_get_init_creds_opt_free(k5->ctx, options); if (my_creds.client == k5->me) { my_creds.client = 0; } if (opts->pa_opts) { free(opts->pa_opts); opts->pa_opts = NULL; opts->num_pa_opts = 0; } krb5_free_cred_contents(k5->ctx, &my_creds); if (keytab) krb5_kt_close(k5->ctx, keytab); return notix?0:1; } static int k4_kinit(opts, k4, ctx) struct k_opts* opts; struct k4_data* k4; krb5_context ctx; { #ifdef KRB5_KRB4_COMPAT char* progname = progname_v4; int k_errno = 0; #endif if (!got_k4) return 0; if (opts->starttime) return 0; #ifdef KRB5_KRB4_COMPAT if (!k4->lifetime) k4->lifetime = opts->lifetime; if (!k4->lifetime) k4->lifetime = KRB4_BACKUP_DEFAULT_LIFE_SECS; k4->lifetime = krb_time_to_life(0, k4->lifetime); switch (opts->action) { case INIT_PW: if (!got_password) { unsigned int pwsize = sizeof(stash_password); krb5_error_code code; char prompt[1024]; sprintf(prompt, gettext("Password for %s: "), opts->principal_name); stash_password[0] = 0; /* Note: krb5_read_password does not actually look at the context, so we're ok even if we don't have a context. If we cannot dynamically load krb5, we can substitute any decent read password function instead of the krb5 one. */ code = krb5_read_password(ctx, prompt, 0, stash_password, &pwsize); if (code || pwsize == 0) { fprintf(stderr, gettext("Error while reading password for '%s'\n"), opts->principal_name); memset(stash_password, 0, sizeof(stash_password)); return 0; } got_password = 1; } k_errno = krb_get_pw_in_tkt(k4->aname, k4->inst, k4->realm, "krbtgt", k4->realm, k4->lifetime, stash_password); if (k_errno) { fprintf(stderr, "%s: %s\n", progname, krb_get_err_text(k_errno)); if (authed_k5) fprintf(stderr, gettext("Maybe your KDC does not support v4. " "Try the -5 option next time.\n")); return 0; } return 1; #ifndef HAVE_KRB524 case INIT_KT: fprintf(stderr, gettext("%s: srvtabs are not supported\n"), progname); return 0; case RENEW: fprintf(stderr, gettext("%s: renewal of krb4 tickets is not supported\n"), progname); return 0; #else /* These cases are handled by the 524 code - this prevents the compiler warnings of not using all the enumerated types. */ case INIT_KT: case RENEW: case VALIDATE: return 0; #endif } #endif return 0; } static char* getvprogname(v, progname) char *v, *progname; { unsigned int len = strlen(progname) + 2 + strlen(v) + 2; char *ret = malloc(len); if (ret) sprintf(ret, "%s(v%s)", progname, v); else ret = progname; return ret; } #ifdef HAVE_KRB524 /* Convert krb5 tickets to krb4. */ static int try_convert524(k5) struct k5_data* k5; { char * progname = progname_v524; krb5_error_code code = 0; int icode = 0; krb5_principal kpcserver = 0; krb5_creds *v5creds = 0; krb5_creds increds; CREDENTIALS v4creds; if (!got_k4 || !got_k5) return 0; memset((char *) &increds, 0, sizeof(increds)); /* From this point on, we can goto cleanup because increds is initialized. */ if ((code = krb5_build_principal(k5->ctx, &kpcserver, krb5_princ_realm(k5->ctx, k5->me)->length, krb5_princ_realm(k5->ctx, k5->me)->data, "krbtgt", krb5_princ_realm(k5->ctx, k5->me)->data, NULL))) { com_err(progname, code, gettext( "while creating service principal name")); goto cleanup; } increds.client = k5->me; increds.server = kpcserver; /* Prevent duplicate free calls. */ kpcserver = 0; increds.times.endtime = 0; increds.keyblock.enctype = ENCTYPE_DES_CBC_CRC; if ((code = krb5_get_credentials(k5->ctx, 0, k5->cc, &increds, &v5creds))) { com_err(progname, code, gettext("getting V5 credentials")); goto cleanup; } if ((icode = krb524_convert_creds_kdc(k5->ctx, v5creds, &v4creds))) { com_err(progname, icode, gettext("converting to V4 credentials")); goto cleanup; } /* this is stolen from the v4 kinit */ /* initialize ticket cache */ if ((icode = in_tkt(v4creds.pname, v4creds.pinst) != KSUCCESS)) { com_err(progname, icode, gettext( "trying to create the V4 ticket file")); goto cleanup; } /* stash ticket, session key, etc. for future use */ if ((icode = krb_save_credentials(v4creds.service, v4creds.instance, v4creds.realm, v4creds.session, v4creds.lifetime, v4creds.kvno, &(v4creds.ticket_st), v4creds.issue_date))) { com_err(progname, icode, gettext( "trying to save the V4 ticket")); goto cleanup; } cleanup: memset(&v4creds, 0, sizeof(v4creds)); if (v5creds) krb5_free_creds(k5->ctx, v5creds); increds.client = 0; krb5_free_cred_contents(k5->ctx, &increds); if (kpcserver) krb5_free_principal(k5->ctx, kpcserver); return !(code || icode); } #endif /* HAVE_KRB524 */ int main(argc, argv) int argc; char **argv; { struct k_opts opts; struct k5_data k5; struct k4_data k4; char *progname; (void) setlocale(LC_ALL, ""); #if !defined(TEXT_DOMAIN) #define TEXT_DOMAIN "SYS_TEST" #endif (void) textdomain(TEXT_DOMAIN); progname = GET_PROGNAME(argv[0]); progname_v5 = getvprogname("5", progname); #ifdef KRB5_KRB4_COMPAT progname_v4 = getvprogname("4", progname); progname_v524 = getvprogname("524", progname); #endif /* Ensure we can be driven from a pipe */ if(!isatty(fileno(stdin))) setvbuf(stdin, 0, _IONBF, 0); if(!isatty(fileno(stdout))) setvbuf(stdout, 0, _IONBF, 0); if(!isatty(fileno(stderr))) setvbuf(stderr, 0, _IONBF, 0); /* This is where we would put in code to dynamically load Kerberos libraries. Currenlty, we just get them implicitly. */ got_k5 = 1; #ifdef KRB5_KRB4_COMPAT got_k4 = 1; #endif memset(&opts, 0, sizeof(opts)); opts.action = INIT_PW; memset(&k5, 0, sizeof(k5)); memset(&k4, 0, sizeof(k4)); set_com_err_hook (extended_com_err_fn); parse_options(argc, argv, &opts, progname); got_k5 = k5_begin(&opts, &k5, &k4); got_k4 = k4_begin(&opts, &k4); authed_k5 = k5_kinit(&opts, &k5); #ifdef HAVE_KRB524 if (authed_k5) authed_k4 = try_convert524(&k5); #endif if (!authed_k4) authed_k4 = k4_kinit(&opts, &k4, k5.ctx); #ifdef KRB5_KRB4_COMPAT memset(stash_password, 0, sizeof(stash_password)); #endif if (authed_k5 && opts.verbose) fprintf(stderr, gettext("Authenticated to Kerberos v5\n")); if (authed_k4 && opts.verbose) fprintf(stderr, gettext("Authenticated to Kerberos v4\n")); k5_end(&k5); k4_end(&k4); if ((got_k5 && !authed_k5) || (got_k4 && !authed_k4) || (!got_k5 && !got_k4)) exit(1); return 0; } static void _kwarnd_add_warning(char *progname, char *me, time_t endtime) { if (kwarn_add_warning(me, endtime) != 0) fprintf(stderr, gettext( "%s: no ktkt_warnd warning possible\n"), progname); return; } static void _kwarnd_del_warning(char *progname, char *me) { if (kwarn_del_warning(me) != 0) fprintf(stderr, gettext( "%s: unable to delete ktkt_warnd message for %s\n"), progname, me); return; } # # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Copyright (c) 2018, Joyent, Inc. PROG= klist OUTPUT_OPTION = -I. OBJS = klist.o SRCS = $(OBJS:.o=.c) include ../../Makefile.cmd include $(SRC)/lib/gss_mechs/mech_krb5/Makefile.mech_krb5 TEXT_DOMAIN = SUNW_OST_OSCMD POFILE = klist.po POFILES = generic.po DEFS = -DHAVE_GETIPNODEBYADDR=1 -DHAVE_INET_NTOP=1 CPPFLAGS += -I$(SRC)/lib/gss_mechs/mech_krb5/include \ -I$(SRC)/uts/common/gssapi/mechs/krb5/include \ $(DEFS) LDFLAGS += $(KRUNPATH) LDLIBS += $(KMECHLIB) CERRWARN += -Wno-unused-function # not linted SMATCH=off $(GPROGS) : CPPFLAGS += -DSYSV -DSunOS=50 .KEEP_STATE: all: $(PROG) $(PROG): $(OBJS) $(LINK.c) $(OBJS) -o $@ $(LDLIBS) $(POST_PROCESS) install: $(KRB5PROG) install_h: clean: $(RM) $(OBJS) include ../../Makefile.targ $(POFILE): $(DERIVED_FILES) .WAIT $(POFILES) $(RM) $@ $(CAT) $(POFILES) > $@ generic.po: $(RM) messages.po $(XGETTEXT) $(XGETFLAGS) `($(GREP) -l gettext *.[ch] || echo /dev/null)` $(SED) "/^domain/d" messages.po > $@ $(RM) messages.po /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * clients/klist/klist.c * * Copyright 1990 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. * * * List out the contents of your credential cache or keytab. */ #include #include "com_err.h" #include #ifdef KRB5_KRB4_COMPAT #include #endif /* KRB5_KRB4_COMPAT */ #include #include #include #include #include #include #include #if defined(HAVE_ARPA_INET_H) #include #endif #include #include #ifndef _WIN32 #define GET_PROGNAME(x) (strrchr((x), '/') ? strrchr((x), '/')+1 : (x)) #else #define GET_PROGNAME(x) max(max(strrchr((x), '/'), strrchr((x), '\\')) + 1,(x)) #endif /* _WIN32 */ #ifndef _WIN32 #include #include #endif extern int optind; int show_flags = 0, show_time = 0, status_only = 0, show_keys = 0; int show_etype = 0, show_addresses = 0, no_resolve = 0; char *defname; char *progname; krb5_int32 now; size_t timestamp_width; krb5_context kcontext; char * etype_string (krb5_enctype ); void show_credential (krb5_creds *); void do_ccache (char *); void do_keytab (char *); void printtime (time_t); void one_addr (krb5_address *); void fillit (FILE *, unsigned int, int); void show_addr(krb5_address *a); #ifdef KRB5_KRB4_COMPAT void do_v4_ccache (char *); #endif /* KRB5_KRB4_COMPAT */ #define DEFAULT 0 #define CCACHE 1 #define KEYTAB 2 /* * The reason we start out with got_k4 and got_k5 as zero (false) is * so that we can easily add dynamic loading support for determining * whether Kerberos 4 and Keberos 5 libraries are available */ static int got_k5 = 0; static int got_k4 = 0; static int default_k5 = 1; #ifdef KRB5_KRB4_COMPAT static int default_k4 = 1; #else /* KRB5_KRB4_COMPAT */ static int default_k4 = 0; #endif /* KRB5_KRB4_COMPAT */ static void usage() { #define KRB_AVAIL_STRING(x) ((x)?gettext("available"):gettext("not available")) fprintf(stderr, gettext("Usage: %s [-5] [-4] [-e]" " [[-c] [-f] [-s] [-a [-n]]] " "[-k [-t] [-K]] [name]\n"), progname); fprintf(stderr, "\t-5 Kerberos 5 (%s)\n", KRB_AVAIL_STRING(got_k5)); fprintf(stderr, "\t-4 Kerberos 4 (%s)\n", KRB_AVAIL_STRING(got_k4)); fprintf(stderr, gettext("\t (Default is %s%s%s%s)\n"), default_k5?"Kerberos 5":"", (default_k5 && default_k4)?gettext(" and "):"", default_k4?"Kerberos 4":"", (!default_k5 && !default_k4)?gettext("neither"):""); fprintf(stderr, gettext("\t-c specifies credentials cache\n")); fprintf(stderr, gettext("\t-k specifies keytab\n")); fprintf(stderr, gettext("\t (Default is credentials cache)\n")); fprintf(stderr, gettext("\t-e shows the encryption type\n")); fprintf(stderr, gettext("\toptions for credential caches:\n")); fprintf(stderr, gettext("\t\t-f shows credentials flags\n")); fprintf(stderr, gettext("\t\t-s sets exit status based on valid tgt existence\n")); fprintf(stderr, gettext("\t\t-a displays the address list\n")); fprintf(stderr, gettext("\t\t-n do not reverse-resolve\n")); fprintf(stderr, gettext("\toptions for keytabs:\n")); fprintf(stderr, gettext("\t\t-t shows keytab entry timestamps\n")); fprintf(stderr, gettext("\t\t-K shows keytab entry DES keys\n")); exit(1); } int main(argc, argv) int argc; char **argv; { int c; char *name; int mode; int use_k5 = 0, use_k4 = 0; got_k5 = 1; #ifdef KRB5_KRB4_COMPAT got_k4 = 1; #endif /* KRB5_KRB4_COMPAT */ (void) setlocale(LC_ALL, ""); #if !defined(TEXT_DOMAIN) #define TEXT_DOMAIN "SYS_TEST" #endif /* !TEXT_DOMAIN */ (void) textdomain(TEXT_DOMAIN); progname = GET_PROGNAME(argv[0]); name = NULL; mode = DEFAULT; while ((c = getopt(argc, argv, "fetKsnack45")) != -1) { switch (c) { case 'f': show_flags = 1; break; case 'e': show_etype = 1; break; case 't': show_time = 1; break; case 'K': show_keys = 1; break; case 's': status_only = 1; break; case 'n': no_resolve = 1; break; case 'a': show_addresses = 1; break; case 'c': if (mode != DEFAULT) usage(); mode = CCACHE; break; case 'k': if (mode != DEFAULT) usage(); mode = KEYTAB; break; case '4': if (!got_k4) { #ifdef KRB5_KRB4_COMPAT fprintf(stderr, "Kerberos 4 support could not be loaded\n"); #else /* KRB5_KRB4_COMPAT */ fprintf(stderr, gettext("This was not built with Kerberos 4 support\n")); #endif /* KRB5_KRB4_COMPAT */ exit(3); } use_k4 = 1; break; case '5': if (!got_k5) { fprintf(stderr, gettext("Kerberos 5 support could not be loaded\n")); exit(3); } use_k5 = 1; break; default: usage(); break; } } if (no_resolve && !show_addresses) { usage(); } if (mode == DEFAULT || mode == CCACHE) { if (show_time || show_keys) usage(); } else { if (show_flags || status_only || show_addresses) usage(); } if (argc - optind > 1) { fprintf(stderr, gettext("Extra arguments (starting with \"%s\").\n"), argv[optind+1]); usage(); } name = (optind == argc-1) ? argv[optind] : 0; if (!use_k5 && !use_k4) { use_k5 = default_k5; use_k4 = default_k4; } if (!use_k5) got_k5 = 0; if (!use_k4) got_k4 = 0; now = time(0); { char tmp[BUFSIZ]; if (!krb5_timestamp_to_sfstring(now, tmp, 20, (char *) NULL) || !krb5_timestamp_to_sfstring(now, tmp, sizeof(tmp), (char *) NULL)) timestamp_width = (int) strlen(tmp); else timestamp_width = 15; } if (got_k5) { krb5_error_code retval; retval = krb5_init_context(&kcontext); if (retval) { com_err(progname, retval, gettext("while initializing krb5")); exit(1); } if (mode == DEFAULT || mode == CCACHE) do_ccache(name); else do_keytab(name); } else { #ifdef KRB5_KRB4_COMPAT if (mode == DEFAULT || mode == CCACHE) do_v4_ccache(name); else { /* We may want to add v4 srvtab support */ fprintf(stderr, "%s: srvtab option not supported for Kerberos 4\n", progname); exit(1); } #endif /* KRB4_KRB5_COMPAT */ } return 0; } void do_keytab(name) char *name; { krb5_keytab kt; krb5_keytab_entry entry; krb5_kt_cursor cursor; char buf[BUFSIZ]; /* hopefully large enough for any type */ char *pname; int code; if (name == NULL) { if ((code = krb5_kt_default(kcontext, &kt))) { com_err(progname, code, gettext("while getting default keytab")); exit(1); } } else { if ((code = krb5_kt_resolve(kcontext, name, &kt))) { com_err(progname, code, gettext("while resolving keytab %s"), name); exit(1); } } if ((code = krb5_kt_get_name(kcontext, kt, buf, BUFSIZ))) { com_err(progname, code, gettext("while getting keytab name")); exit(1); } printf(gettext("Keytab name: %s\n"), buf); if ((code = krb5_kt_start_seq_get(kcontext, kt, &cursor))) { com_err(progname, code, gettext("while starting keytab scan")); exit(1); } if (show_time) { printf(gettext("KVNO Timestamp")); fillit(stdout, timestamp_width - sizeof (gettext("Timestamp")) + 2, (int)' '); printf(gettext("Principal\n")); printf("---- "); fillit(stdout, timestamp_width, (int) '-'); printf(" "); fillit(stdout, 78 - timestamp_width - sizeof (gettext("KVNO")), (int)'-'); printf("\n"); } else { printf(gettext("KVNO Principal\n")); printf("---- ------------------------------" "--------------------------------------" "------\n"); } while ((code = krb5_kt_next_entry(kcontext, kt, &entry, &cursor)) == 0) { if ((code = krb5_unparse_name(kcontext, entry.principal, &pname))) { com_err(progname, code, gettext("while unparsing principal name")); exit(1); } printf("%4d ", entry.vno); if (show_time) { printtime(entry.timestamp); printf(" "); } printf("%s", pname); if (show_etype) printf(" (%s) " , etype_string(entry.key.enctype)); if (show_keys) { printf(" (0x"); { int i; for (i = 0; i < entry.key.length; i++) printf("%02x", entry.key.contents[i]); } printf(")"); } printf("\n"); krb5_free_unparsed_name(kcontext, pname); } if (code && code != KRB5_KT_END) { com_err(progname, code, gettext("while scanning keytab")); exit(1); } if ((code = krb5_kt_end_seq_get(kcontext, kt, &cursor))) { com_err(progname, code, gettext("while ending keytab scan")); exit(1); } exit(0); } void do_ccache(name) char *name; { krb5_ccache cache = NULL; krb5_cc_cursor cur; krb5_creds creds; krb5_principal princ; krb5_flags flags; krb5_error_code code; int exit_status = 0; if (status_only) /* exit_status is set back to 0 if a valid tgt is found */ exit_status = 1; if (name == NULL) { if ((code = krb5_cc_default(kcontext, &cache))) { if (!status_only) com_err(progname, code, gettext("while getting default " "ccache")); exit(1); } } else { if ((code = krb5_cc_resolve(kcontext, name, &cache))) { if (!status_only) com_err(progname, code, gettext("while resolving ccache %s"), name); exit(1); } } flags = 0; /* turns off OPENCLOSE mode */ if ((code = krb5_cc_set_flags(kcontext, cache, flags))) { if (code == KRB5_FCC_NOFILE) { if (!status_only) { com_err(progname, code, gettext("(ticket cache %s:%s)"), krb5_cc_get_type(kcontext, cache), krb5_cc_get_name(kcontext, cache)); #ifdef KRB5_KRB4_COMPAT if (name == NULL) do_v4_ccache(0); #endif /* KRB5_KRB4_COMPAT */ } } else { if (!status_only) com_err(progname, code, gettext("while setting cache " "flags(ticket cache %s:%s)"), krb5_cc_get_type(kcontext, cache), krb5_cc_get_name(kcontext, cache)); } exit(1); } if ((code = krb5_cc_get_principal(kcontext, cache, &princ))) { if (!status_only) com_err(progname, code, gettext("while retrieving principal name")); exit(1); } if ((code = krb5_unparse_name(kcontext, princ, &defname))) { if (!status_only) com_err(progname, code, gettext("while unparsing principal name")); exit(1); } if (!status_only) { printf(gettext("Ticket cache: %s:%s\nDefault principal: " "%s\n\n"), krb5_cc_get_type(kcontext, cache), krb5_cc_get_name(kcontext, cache), defname); fputs(gettext("Valid starting"), stdout); fillit(stdout, timestamp_width - sizeof (gettext("Valid starting")) + 3, (int)' '); fputs(gettext("Expires"), stdout); fillit(stdout, timestamp_width - sizeof (gettext("Expires")) + 3, (int)' '); fputs(gettext("Service principal\n"), stdout); } if ((code = krb5_cc_start_seq_get(kcontext, cache, &cur))) { if (!status_only) com_err(progname, code, gettext("while starting to retrieve tickets")); exit(1); } while (!(code = krb5_cc_next_cred(kcontext, cache, &cur, &creds))) { if (status_only) { if (exit_status && creds.server->length == 2 && strcmp(creds.server->realm.data, princ->realm.data) == 0 && strcmp((char *)creds.server->data[0].data, "krbtgt") == 0 && strcmp((char *)creds.server->data[1].data, princ->realm.data) == 0 && creds.times.endtime > now) exit_status = 0; } else { show_credential(&creds); } krb5_free_cred_contents(kcontext, &creds); } if (code == KRB5_CC_END) { if ((code = krb5_cc_end_seq_get(kcontext, cache, &cur))) { if (!status_only) com_err(progname, code, gettext("while finishing ticket " "retrieval")); exit(1); } flags = KRB5_TC_OPENCLOSE; /* turns on OPENCLOSE mode */ if ((code = krb5_cc_set_flags(kcontext, cache, flags))) { if (!status_only) com_err(progname, code, gettext("while closing ccache")); exit(1); } #ifdef KRB5_KRB4_COMPAT if (name == NULL && !status_only) do_v4_ccache(0); #endif /* KRB5_KRB4_COMPAT */ exit(exit_status); } else { if (!status_only) com_err(progname, code, gettext("while retrieving a ticket")); exit(1); } } char * etype_string(enctype) krb5_enctype enctype; { static char buf[256]; krb5_error_code retval; if ((retval = krb5_enctype_to_string(enctype, buf, sizeof(buf)))) { /* XXX if there's an error != EINVAL, I should probably report it */ snprintf(buf, sizeof(buf), gettext("unsupported encryption type %d"), enctype); } return buf; } static char * flags_string(cred) register krb5_creds *cred; { static char buf[32]; int i = 0; if (cred->ticket_flags & TKT_FLG_FORWARDABLE) buf[i++] = 'F'; if (cred->ticket_flags & TKT_FLG_FORWARDED) buf[i++] = 'f'; if (cred->ticket_flags & TKT_FLG_PROXIABLE) buf[i++] = 'P'; if (cred->ticket_flags & TKT_FLG_PROXY) buf[i++] = 'p'; if (cred->ticket_flags & TKT_FLG_MAY_POSTDATE) buf[i++] = 'D'; if (cred->ticket_flags & TKT_FLG_POSTDATED) buf[i++] = 'd'; if (cred->ticket_flags & TKT_FLG_INVALID) buf[i++] = 'i'; if (cred->ticket_flags & TKT_FLG_RENEWABLE) buf[i++] = 'R'; if (cred->ticket_flags & TKT_FLG_INITIAL) buf[i++] = 'I'; if (cred->ticket_flags & TKT_FLG_HW_AUTH) buf[i++] = 'H'; if (cred->ticket_flags & TKT_FLG_PRE_AUTH) buf[i++] = 'A'; if (cred->ticket_flags & TKT_FLG_TRANSIT_POLICY_CHECKED) buf[i++] = 'T'; if (cred->ticket_flags & TKT_FLG_OK_AS_DELEGATE) buf[i++] = 'O'; /* D/d are taken. Use short strings? */ if (cred->ticket_flags & TKT_FLG_ANONYMOUS) buf[i++] = 'a'; buf[i] = '\0'; return(buf); } void printtime(tv) time_t tv; { char timestring[BUFSIZ]; char fill; fill = ' '; if (!krb5_timestamp_to_sfstring((krb5_timestamp) tv, timestring, timestamp_width+1, &fill)) { printf(timestring); } } void show_credential(cred) register krb5_creds * cred; { krb5_error_code retval; krb5_ticket *tkt; char *name, *sname, *flags; int extra_field = 0; retval = krb5_unparse_name(kcontext, cred->client, &name); if (retval) { com_err(progname, retval, gettext("while unparsing client name")); return; } retval = krb5_unparse_name(kcontext, cred->server, &sname); if (retval) { com_err(progname, retval, gettext("while unparsing server name")); krb5_free_unparsed_name(kcontext, name); return; } if (!cred->times.starttime) cred->times.starttime = cred->times.authtime; printtime(cred->times.starttime); putchar(' '); putchar(' '); printtime(cred->times.endtime); putchar(' '); putchar(' '); printf("%s\n", sname); if (strcmp(name, defname)) { printf(gettext("\tfor client %s"), name); extra_field++; } if (cred->times.renew_till) { if (!extra_field) fputs("\t",stdout); else fputs(", ",stdout); fputs(gettext("renew until "), stdout); printtime(cred->times.renew_till); extra_field += 2; } if (extra_field > 3) { fputs("\n", stdout); extra_field = 0; } if (show_flags) { flags = flags_string(cred); if (flags && *flags) { if (!extra_field) fputs("\t",stdout); else fputs(", ",stdout); printf(gettext("Flags: %s"), flags); extra_field++; } } if (extra_field > 2) { fputs("\n", stdout); extra_field = 0; } if (show_etype) { retval = decode_krb5_ticket(&cred->ticket, &tkt); if (retval) goto err_tkt; if (!extra_field) fputs("\t",stdout); else fputs(", ",stdout); printf(gettext("Etype(skey, tkt): %s, "), etype_string(cred->keyblock.enctype)); printf("%s ", etype_string(tkt->enc_part.enctype)); extra_field++; err_tkt: if (tkt != NULL) krb5_free_ticket(kcontext, tkt); } /* if any additional info was printed, extra_field is non-zero */ if (extra_field) putchar('\n'); if (show_addresses) { if (!cred->addresses || !cred->addresses[0]) { printf(gettext("\tAddresses: (none)\n")); } else { int i; printf(gettext("\tAddresses: ")); one_addr(cred->addresses[0]); for (i=1; cred->addresses[i]; i++) { printf(", "); one_addr(cred->addresses[i]); } printf("\n"); } } krb5_free_unparsed_name(kcontext, name); krb5_free_unparsed_name(kcontext, sname); } #include "port-sockets.h" #include "socket-utils.h" /* for ss2sin etc */ #include void one_addr(a) krb5_address *a; { struct sockaddr_storage ss; int err; char namebuf[NI_MAXHOST]; memset (&ss, 0, sizeof (ss)); switch (a->addrtype) { case ADDRTYPE_INET: if (a->length != IPV4_ADDR_LEN) { broken: printf ("broken address (type %d length %d)", a->addrtype, a->length); return; } { struct sockaddr_in *sinp = ss2sin (&ss); sinp->sin_family = AF_INET; #ifdef HAVE_SA_LEN sinp->sin_len = sizeof (struct sockaddr_in); #endif memcpy (&sinp->sin_addr, a->contents, IPV4_ADDR_LEN); } break; #ifdef KRB5_USE_INET6 case ADDRTYPE_INET6: if (a->length != IPV6_ADDR_LEN) goto broken; { struct sockaddr_in6 *sin6p = ss2sin6 (&ss); sin6p->sin6_family = AF_INET6; #ifdef HAVE_SA_LEN sin6p->sin6_len = sizeof (struct sockaddr_in6); #endif memcpy (&sin6p->sin6_addr, a->contents, IPV6_ADDR_LEN); } break; #endif default: printf(gettext("unknown addr type %d"), a->addrtype); return; } namebuf[0] = 0; err = getnameinfo (ss2sa (&ss), socklen (ss2sa (&ss)), namebuf, sizeof (namebuf), 0, 0, no_resolve ? NI_NUMERICHOST : 0U); if (err) { printf (gettext("unprintable address (type %d, error %d %s)"), a->addrtype, err, gai_strerror (err)); return; } printf ("%s", namebuf); } void fillit(f, num, c) FILE *f; unsigned int num; int c; { int i; for (i=0; i $@ include ../../Makefile.cmd include $(SRC)/lib/gss_mechs/mech_krb5/Makefile.mech_krb5 TEXT_DOMAIN = SUNW_OST_OSCMD POFILE = kproplog.po POFILES = generic.po LDFLAGS += -R$(KRB5RUNPATH) $(KRUNPATH) # Hammerhead: GNU ld needs rpath-link for transitive deps (libdyn, libkdb) via libkadm5clnt LDFLAGS += -Wl,-rpath-link,$(ROOT)/usr/lib/krb5 LDLIBS += -L$(KRB5LIB) $(KLIB) -lnsl -lkdb -lkadm5clnt CPPFLAGS += -I. -I$(SRC)/lib/krb5 \ -I$(KRB5IPROPDIR) \ -I$(SRC)/lib/gss_mechs/mech_krb5/include \ -I$(SRC)/uts/common/gssapi/mechs/krb5/include CERRWARN += -Wno-unused-variable CERRWARN += -Wno-unused-function CERRWARN += -Wno-implicit-function-declaration # Hammerhead: Suppress xdr_longlong_t type mismatch warnings (iprop_xdr.c) CERRWARN += -Wno-incompatible-pointer-types # not linted SMATCH=off all: $(PROG) $(PROG): $(OBJS) $(DERIVED_OBJS) $(LINK.c) $(OBJS) $(DERIVED_OBJS) -o $@ $(LDLIBS) $(POST_PROCESS) # Hammerhead: pre-generated (rpcgen + GNU cpp truncation bug) # iprop.h and iprop_xdr.c are now committed source files. # Explicitly state the dependancy on iprop.h $(OBJS): iprop.h install: $(KRB5SBINPROG) clean: $(RM) $(OBJS) $(DERIVED_OBJS) @# Hammerhead: do not delete pre-generated $(ISRC) include ../../Makefile.targ $(POFILE): $(DERIVED_FILES) .WAIT $(POFILES) $(RM) $@ $(CAT) $(POFILES) > $@ generic.po: $(RM) messages.po $(XGETTEXT) $(XGETFLAGS) `$(GREP) -l gettext *.[ch]` $(SED) "/^domain/d" messages.po > $@ $(RM) messages.po /* * Please do not edit this file. * It was generated using rpcgen. */ #ifndef _IPROP_H_RPCGEN #define _IPROP_H_RPCGEN #include typedef struct { u_int utf8str_t_len; char *utf8str_t_val; } utf8str_t; typedef uint32_t kdb_sno_t; struct kdbe_time_t { uint32_t seconds; uint32_t useconds; }; typedef struct kdbe_time_t kdbe_time_t; struct kdbe_key_t { int32_t k_ver; int32_t k_kvno; struct { u_int k_enctype_len; int32_t *k_enctype_val; } k_enctype; struct { u_int k_contents_len; utf8str_t *k_contents_val; } k_contents; }; typedef struct kdbe_key_t kdbe_key_t; struct kdbe_data_t { int32_t k_magic; utf8str_t k_data; }; typedef struct kdbe_data_t kdbe_data_t; struct kdbe_princ_t { utf8str_t k_realm; struct { u_int k_components_len; kdbe_data_t *k_components_val; } k_components; int32_t k_nametype; }; typedef struct kdbe_princ_t kdbe_princ_t; struct kdbe_tl_t { int16_t tl_type; struct { u_int tl_data_len; char *tl_data_val; } tl_data; }; typedef struct kdbe_tl_t kdbe_tl_t; typedef struct { u_int kdbe_pw_hist_t_len; kdbe_key_t *kdbe_pw_hist_t_val; } kdbe_pw_hist_t; enum kdbe_attr_type_t { AT_ATTRFLAGS = 0, AT_MAX_LIFE = 1, AT_MAX_RENEW_LIFE = 2, AT_EXP = 3, AT_PW_EXP = 4, AT_LAST_SUCCESS = 5, AT_LAST_FAILED = 6, AT_FAIL_AUTH_COUNT = 7, AT_PRINC = 8, AT_KEYDATA = 9, AT_TL_DATA = 10, AT_LEN = 11, AT_MOD_PRINC = 12, AT_MOD_TIME = 13, AT_MOD_WHERE = 14, AT_PW_LAST_CHANGE = 15, AT_PW_POLICY = 16, AT_PW_POLICY_SWITCH = 17, AT_PW_HIST_KVNO = 18, AT_PW_HIST = 19 }; typedef enum kdbe_attr_type_t kdbe_attr_type_t; struct kdbe_val_t { kdbe_attr_type_t av_type; union { uint32_t av_attrflags; uint32_t av_max_life; uint32_t av_max_renew_life; uint32_t av_exp; uint32_t av_pw_exp; uint32_t av_last_success; uint32_t av_last_failed; uint32_t av_fail_auth_count; kdbe_princ_t av_princ; struct { u_int av_keydata_len; kdbe_key_t *av_keydata_val; } av_keydata; struct { u_int av_tldata_len; kdbe_tl_t *av_tldata_val; } av_tldata; int16_t av_len; uint32_t av_pw_last_change; kdbe_princ_t av_mod_princ; uint32_t av_mod_time; utf8str_t av_mod_where; utf8str_t av_pw_policy; bool_t av_pw_policy_switch; uint32_t av_pw_hist_kvno; struct { u_int av_pw_hist_len; kdbe_pw_hist_t *av_pw_hist_val; } av_pw_hist; struct { u_int av_extension_len; char *av_extension_val; } av_extension; } kdbe_val_t_u; }; typedef struct kdbe_val_t kdbe_val_t; typedef struct { u_int kdbe_t_len; kdbe_val_t *kdbe_t_val; } kdbe_t; struct kdb_incr_update_t { utf8str_t kdb_princ_name; kdb_sno_t kdb_entry_sno; kdbe_time_t kdb_time; kdbe_t kdb_update; bool_t kdb_deleted; bool_t kdb_commit; struct { u_int kdb_kdcs_seen_by_len; utf8str_t *kdb_kdcs_seen_by_val; } kdb_kdcs_seen_by; struct { u_int kdb_futures_len; char *kdb_futures_val; } kdb_futures; }; typedef struct kdb_incr_update_t kdb_incr_update_t; typedef struct { u_int kdb_ulog_t_len; kdb_incr_update_t *kdb_ulog_t_val; } kdb_ulog_t; enum update_status_t { UPDATE_OK = 0, UPDATE_ERROR = 1, UPDATE_FULL_RESYNC_NEEDED = 2, UPDATE_BUSY = 3, UPDATE_NIL = 4, UPDATE_PERM_DENIED = 5 }; typedef enum update_status_t update_status_t; struct kdb_last_t { kdb_sno_t last_sno; kdbe_time_t last_time; }; typedef struct kdb_last_t kdb_last_t; struct kdb_incr_result_t { kdb_last_t lastentry; kdb_ulog_t updates; update_status_t ret; }; typedef struct kdb_incr_result_t kdb_incr_result_t; struct kdb_fullresync_result_t { kdb_last_t lastentry; update_status_t ret; }; typedef struct kdb_fullresync_result_t kdb_fullresync_result_t; #define KRB5_IPROP_PROG 100423 #define KRB5_IPROP_VERS 1 #define IPROP_NULL 0 extern void * iprop_null_1(); #define IPROP_GET_UPDATES 1 extern kdb_incr_result_t * iprop_get_updates_1(); #define IPROP_FULL_RESYNC 2 extern kdb_fullresync_result_t * iprop_full_resync_1(); extern int krb5_iprop_prog_1_freeresult(); /* the xdr functions */ extern bool_t xdr_utf8str_t(); extern bool_t xdr_kdb_sno_t(); extern bool_t xdr_kdbe_time_t(); extern bool_t xdr_kdbe_key_t(); extern bool_t xdr_kdbe_data_t(); extern bool_t xdr_kdbe_princ_t(); extern bool_t xdr_kdbe_tl_t(); extern bool_t xdr_kdbe_pw_hist_t(); extern bool_t xdr_kdbe_attr_type_t(); extern bool_t xdr_kdbe_val_t(); extern bool_t xdr_kdbe_t(); extern bool_t xdr_kdb_incr_update_t(); extern bool_t xdr_kdb_ulog_t(); extern bool_t xdr_update_status_t(); extern bool_t xdr_kdb_last_t(); extern bool_t xdr_kdb_incr_result_t(); extern bool_t xdr_kdb_fullresync_result_t(); #endif /* !_IPROP_H_RPCGEN */ /* * Please do not edit this file. * It was generated using rpcgen. */ #include "iprop.h" #ifndef _KERNEL #include #endif /* !_KERNEL */ #include "iprop.h" bool_t xdr_int16_t(xdrs, objp) XDR *xdrs; int16_t *objp; { rpc_inline_t *buf __unused; if (!xdr_short(xdrs, objp)) return (FALSE); return (TRUE); } bool_t xdr_uint16_t(xdrs, objp) XDR *xdrs; uint16_t *objp; { rpc_inline_t *buf __unused; if (!xdr_u_short(xdrs, objp)) return (FALSE); return (TRUE); } bool_t xdr_int32_t(xdrs, objp) XDR *xdrs; int32_t *objp; { rpc_inline_t *buf __unused; if (!xdr_int(xdrs, objp)) return (FALSE); return (TRUE); } bool_t xdr_uint32_t(xdrs, objp) XDR *xdrs; uint32_t *objp; { rpc_inline_t *buf __unused; if (!xdr_u_int(xdrs, objp)) return (FALSE); return (TRUE); } bool_t xdr_int64_t(xdrs, objp) XDR *xdrs; int64_t *objp; { rpc_inline_t *buf __unused; if (!xdr_longlong_t(xdrs, objp)) return (FALSE); return (TRUE); } bool_t xdr_uint64_t(xdrs, objp) XDR *xdrs; uint64_t *objp; { rpc_inline_t *buf __unused; if (!xdr_u_longlong_t(xdrs, objp)) return (FALSE); return (TRUE); } bool_t xdr_utf8str_t(xdrs, objp) XDR *xdrs; utf8str_t *objp; { rpc_inline_t *buf __unused; if (!xdr_bytes(xdrs, (char **)&objp->utf8str_t_val, (u_int *) &objp->utf8str_t_len, ~0)) return (FALSE); return (TRUE); } bool_t xdr_kdb_sno_t(xdrs, objp) XDR *xdrs; kdb_sno_t *objp; { rpc_inline_t *buf __unused; if (!xdr_uint32_t(xdrs, objp)) return (FALSE); return (TRUE); } bool_t xdr_kdbe_time_t(xdrs, objp) XDR *xdrs; kdbe_time_t *objp; { rpc_inline_t *buf __unused; if (!xdr_uint32_t(xdrs, &objp->seconds)) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->useconds)) return (FALSE); return (TRUE); } bool_t xdr_kdbe_key_t(xdrs, objp) XDR *xdrs; kdbe_key_t *objp; { rpc_inline_t *buf __unused; if (!xdr_int32_t(xdrs, &objp->k_ver)) return (FALSE); if (!xdr_int32_t(xdrs, &objp->k_kvno)) return (FALSE); if (!xdr_array(xdrs, (char **)&objp->k_enctype.k_enctype_val, (u_int *) &objp->k_enctype.k_enctype_len, ~0, sizeof (int32_t), (xdrproc_t)xdr_int32_t)) return (FALSE); if (!xdr_array(xdrs, (char **)&objp->k_contents.k_contents_val, (u_int *) &objp->k_contents.k_contents_len, ~0, sizeof (utf8str_t), (xdrproc_t)xdr_utf8str_t)) return (FALSE); return (TRUE); } bool_t xdr_kdbe_data_t(xdrs, objp) XDR *xdrs; kdbe_data_t *objp; { rpc_inline_t *buf __unused; if (!xdr_int32_t(xdrs, &objp->k_magic)) return (FALSE); if (!xdr_utf8str_t(xdrs, &objp->k_data)) return (FALSE); return (TRUE); } bool_t xdr_kdbe_princ_t(xdrs, objp) XDR *xdrs; kdbe_princ_t *objp; { rpc_inline_t *buf __unused; if (!xdr_utf8str_t(xdrs, &objp->k_realm)) return (FALSE); if (!xdr_array(xdrs, (char **)&objp->k_components.k_components_val, (u_int *) &objp->k_components.k_components_len, ~0, sizeof (kdbe_data_t), (xdrproc_t)xdr_kdbe_data_t)) return (FALSE); if (!xdr_int32_t(xdrs, &objp->k_nametype)) return (FALSE); return (TRUE); } bool_t xdr_kdbe_tl_t(xdrs, objp) XDR *xdrs; kdbe_tl_t *objp; { rpc_inline_t *buf __unused; if (!xdr_int16_t(xdrs, &objp->tl_type)) return (FALSE); if (!xdr_bytes(xdrs, (char **)&objp->tl_data.tl_data_val, (u_int *) &objp->tl_data.tl_data_len, ~0)) return (FALSE); return (TRUE); } bool_t xdr_kdbe_pw_hist_t(xdrs, objp) XDR *xdrs; kdbe_pw_hist_t *objp; { rpc_inline_t *buf __unused; if (!xdr_array(xdrs, (char **)&objp->kdbe_pw_hist_t_val, (u_int *) &objp->kdbe_pw_hist_t_len, ~0, sizeof (kdbe_key_t), (xdrproc_t)xdr_kdbe_key_t)) return (FALSE); return (TRUE); } bool_t xdr_kdbe_attr_type_t(xdrs, objp) XDR *xdrs; kdbe_attr_type_t *objp; { rpc_inline_t *buf __unused; if (!xdr_enum(xdrs, (enum_t *)objp)) return (FALSE); return (TRUE); } bool_t xdr_kdbe_val_t(xdrs, objp) XDR *xdrs; kdbe_val_t *objp; { rpc_inline_t *buf __unused; if (!xdr_kdbe_attr_type_t(xdrs, &objp->av_type)) return (FALSE); switch (objp->av_type) { case AT_ATTRFLAGS: if (!xdr_uint32_t(xdrs, &objp->kdbe_val_t_u.av_attrflags)) return (FALSE); break; case AT_MAX_LIFE: if (!xdr_uint32_t(xdrs, &objp->kdbe_val_t_u.av_max_life)) return (FALSE); break; case AT_MAX_RENEW_LIFE: if (!xdr_uint32_t(xdrs, &objp->kdbe_val_t_u.av_max_renew_life)) return (FALSE); break; case AT_EXP: if (!xdr_uint32_t(xdrs, &objp->kdbe_val_t_u.av_exp)) return (FALSE); break; case AT_PW_EXP: if (!xdr_uint32_t(xdrs, &objp->kdbe_val_t_u.av_pw_exp)) return (FALSE); break; case AT_LAST_SUCCESS: if (!xdr_uint32_t(xdrs, &objp->kdbe_val_t_u.av_last_success)) return (FALSE); break; case AT_LAST_FAILED: if (!xdr_uint32_t(xdrs, &objp->kdbe_val_t_u.av_last_failed)) return (FALSE); break; case AT_FAIL_AUTH_COUNT: if (!xdr_uint32_t(xdrs, &objp->kdbe_val_t_u.av_fail_auth_count)) return (FALSE); break; case AT_PRINC: if (!xdr_kdbe_princ_t(xdrs, &objp->kdbe_val_t_u.av_princ)) return (FALSE); break; case AT_KEYDATA: if (!xdr_array(xdrs, (char **)&objp->kdbe_val_t_u.av_keydata.av_keydata_val, (u_int *) &objp->kdbe_val_t_u.av_keydata.av_keydata_len, ~0, sizeof (kdbe_key_t), (xdrproc_t)xdr_kdbe_key_t)) return (FALSE); break; case AT_TL_DATA: if (!xdr_array(xdrs, (char **)&objp->kdbe_val_t_u.av_tldata.av_tldata_val, (u_int *) &objp->kdbe_val_t_u.av_tldata.av_tldata_len, ~0, sizeof (kdbe_tl_t), (xdrproc_t)xdr_kdbe_tl_t)) return (FALSE); break; case AT_LEN: if (!xdr_int16_t(xdrs, &objp->kdbe_val_t_u.av_len)) return (FALSE); break; case AT_PW_LAST_CHANGE: if (!xdr_uint32_t(xdrs, &objp->kdbe_val_t_u.av_pw_last_change)) return (FALSE); break; case AT_MOD_PRINC: if (!xdr_kdbe_princ_t(xdrs, &objp->kdbe_val_t_u.av_mod_princ)) return (FALSE); break; case AT_MOD_TIME: if (!xdr_uint32_t(xdrs, &objp->kdbe_val_t_u.av_mod_time)) return (FALSE); break; case AT_MOD_WHERE: if (!xdr_utf8str_t(xdrs, &objp->kdbe_val_t_u.av_mod_where)) return (FALSE); break; case AT_PW_POLICY: if (!xdr_utf8str_t(xdrs, &objp->kdbe_val_t_u.av_pw_policy)) return (FALSE); break; case AT_PW_POLICY_SWITCH: if (!xdr_bool(xdrs, &objp->kdbe_val_t_u.av_pw_policy_switch)) return (FALSE); break; case AT_PW_HIST_KVNO: if (!xdr_uint32_t(xdrs, &objp->kdbe_val_t_u.av_pw_hist_kvno)) return (FALSE); break; case AT_PW_HIST: if (!xdr_array(xdrs, (char **)&objp->kdbe_val_t_u.av_pw_hist.av_pw_hist_val, (u_int *) &objp->kdbe_val_t_u.av_pw_hist.av_pw_hist_len, ~0, sizeof (kdbe_pw_hist_t), (xdrproc_t)xdr_kdbe_pw_hist_t)) return (FALSE); break; default: if (!xdr_bytes(xdrs, (char **)&objp->kdbe_val_t_u.av_extension.av_extension_val, (u_int *) &objp->kdbe_val_t_u.av_extension.av_extension_len, ~0)) return (FALSE); break; } return (TRUE); } bool_t xdr_kdbe_t(xdrs, objp) XDR *xdrs; kdbe_t *objp; { rpc_inline_t *buf __unused; if (!xdr_array(xdrs, (char **)&objp->kdbe_t_val, (u_int *) &objp->kdbe_t_len, ~0, sizeof (kdbe_val_t), (xdrproc_t)xdr_kdbe_val_t)) return (FALSE); return (TRUE); } bool_t xdr_kdb_incr_update_t(xdrs, objp) XDR *xdrs; kdb_incr_update_t *objp; { rpc_inline_t *buf __unused; if (!xdr_utf8str_t(xdrs, &objp->kdb_princ_name)) return (FALSE); if (!xdr_kdb_sno_t(xdrs, &objp->kdb_entry_sno)) return (FALSE); if (!xdr_kdbe_time_t(xdrs, &objp->kdb_time)) return (FALSE); if (!xdr_kdbe_t(xdrs, &objp->kdb_update)) return (FALSE); if (!xdr_bool(xdrs, &objp->kdb_deleted)) return (FALSE); if (!xdr_bool(xdrs, &objp->kdb_commit)) return (FALSE); if (!xdr_array(xdrs, (char **)&objp->kdb_kdcs_seen_by.kdb_kdcs_seen_by_val, (u_int *) &objp->kdb_kdcs_seen_by.kdb_kdcs_seen_by_len, ~0, sizeof (utf8str_t), (xdrproc_t)xdr_utf8str_t)) return (FALSE); if (!xdr_bytes(xdrs, (char **)&objp->kdb_futures.kdb_futures_val, (u_int *) &objp->kdb_futures.kdb_futures_len, ~0)) return (FALSE); return (TRUE); } bool_t xdr_kdb_ulog_t(xdrs, objp) XDR *xdrs; kdb_ulog_t *objp; { rpc_inline_t *buf __unused; if (!xdr_array(xdrs, (char **)&objp->kdb_ulog_t_val, (u_int *) &objp->kdb_ulog_t_len, ~0, sizeof (kdb_incr_update_t), (xdrproc_t)xdr_kdb_incr_update_t)) return (FALSE); return (TRUE); } bool_t xdr_update_status_t(xdrs, objp) XDR *xdrs; update_status_t *objp; { rpc_inline_t *buf __unused; if (!xdr_enum(xdrs, (enum_t *)objp)) return (FALSE); return (TRUE); } bool_t xdr_kdb_last_t(xdrs, objp) XDR *xdrs; kdb_last_t *objp; { rpc_inline_t *buf __unused; if (!xdr_kdb_sno_t(xdrs, &objp->last_sno)) return (FALSE); if (!xdr_kdbe_time_t(xdrs, &objp->last_time)) return (FALSE); return (TRUE); } bool_t xdr_kdb_incr_result_t(xdrs, objp) XDR *xdrs; kdb_incr_result_t *objp; { rpc_inline_t *buf __unused; if (!xdr_kdb_last_t(xdrs, &objp->lastentry)) return (FALSE); if (!xdr_kdb_ulog_t(xdrs, &objp->updates)) return (FALSE); if (!xdr_update_status_t(xdrs, &objp->ret)) return (FALSE); return (TRUE); } bool_t xdr_kdb_fullresync_result_t(xdrs, objp) XDR *xdrs; kdb_fullresync_result_t *objp; { rpc_inline_t *buf __unused; if (!xdr_kdb_last_t(xdrs, &objp->lastentry)) return (FALSE); if (!xdr_update_status_t(xdrs, &objp->ret)) return (FALSE); return (TRUE); } /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * This module will parse the update logs on the master or slave servers. */ #include #include #include #include #include #include #include #include #include static char *progname; static void usage() { (void) fprintf(stderr, gettext("\nUsage: %s [-h] [-v] [-e num]\n\n"), progname); exit(1); } /* * Print the individual types if verbose mode was specified. */ static void print_attr(kdbe_attr_type_t type) { switch (type) { case AT_ATTRFLAGS: (void) printf(gettext("\t\tAttribute flags\n")); break; case AT_MAX_LIFE: (void) printf(gettext("\t\tMaximum ticket life\n")); break; case AT_MAX_RENEW_LIFE: (void) printf(gettext("\t\tMaximum renewable life\n")); break; case AT_EXP: (void) printf(gettext("\t\tPrincipal expiration\n")); break; case AT_PW_EXP: (void) printf(gettext("\t\tPassword expiration\n")); break; case AT_LAST_SUCCESS: (void) printf(gettext("\t\tLast successful auth\n")); break; case AT_LAST_FAILED: (void) printf(gettext("\t\tLast failed auth\n")); break; case AT_FAIL_AUTH_COUNT: (void) printf(gettext("\t\tFailed passwd attempt\n")); break; case AT_PRINC: (void) printf(gettext("\t\tPrincipal\n")); break; case AT_KEYDATA: (void) printf(gettext("\t\tKey data\n")); break; case AT_TL_DATA: (void) printf(gettext("\t\tTL data\n")); break; case AT_LEN: (void) printf(gettext("\t\tLength\n")); break; case AT_MOD_PRINC: (void) printf(gettext("\t\tModifying principal\n")); break; case AT_MOD_TIME: (void) printf(gettext("\t\tModification time\n")); break; case AT_MOD_WHERE: (void) printf(gettext("\t\tModified where\n")); break; case AT_PW_LAST_CHANGE: (void) printf(gettext("\t\tPassword last changed\n")); break; case AT_PW_POLICY: (void) printf(gettext("\t\tPassword policy\n")); break; case AT_PW_POLICY_SWITCH: (void) printf(gettext("\t\tPassword policy switch\n")); break; case AT_PW_HIST_KVNO: (void) printf(gettext("\t\tPassword history KVNO\n")); break; case AT_PW_HIST: (void) printf(gettext("\t\tPassword history\n")); break; } /* switch */ } /* * Print the update entry information */ static void print_update(kdb_hlog_t *ulog, uint32_t entry, bool_t verbose) { XDR xdrs; uint32_t start_sno, i, j, indx; char *dbprinc; kdb_ent_header_t *indx_log; kdb_incr_update_t upd; if (entry && (entry < ulog->kdb_num)) start_sno = ulog->kdb_last_sno - entry; else start_sno = ulog->kdb_first_sno - 1; for (i = start_sno; i < ulog->kdb_last_sno; i++) { indx = i % ulog->kdb_num; indx_log = (kdb_ent_header_t *)INDEX(ulog, indx); /* * Check for corrupt update entry */ if (indx_log->kdb_umagic != KDB_UMAGIC) { (void) fprintf(stderr, gettext("Corrupt update entry\n\n")); exit(1); } (void) memset((char *)&upd, 0, sizeof (kdb_incr_update_t)); xdrmem_create(&xdrs, (char *)indx_log->entry_data, indx_log->kdb_entry_size, XDR_DECODE); if (!xdr_kdb_incr_update_t(&xdrs, &upd)) { (void) printf(gettext("Entry data decode failure\n\n")); exit(1); } (void) printf("---\n"); (void) printf(gettext("Update Entry\n")); (void) printf(gettext("\tUpdate serial # : %u\n"), indx_log->kdb_entry_sno); (void) printf(gettext("\tUpdate operation : ")); if (upd.kdb_deleted) (void) printf(gettext("Delete\n")); else (void) printf(gettext("Add\n")); dbprinc = malloc(upd.kdb_princ_name.utf8str_t_len + 1); if (dbprinc == NULL) { (void) printf(gettext("Could not allocate " "principal name\n\n")); exit(1); } (void) strlcpy(dbprinc, upd.kdb_princ_name.utf8str_t_val, (upd.kdb_princ_name.utf8str_t_len + 1)); (void) printf(gettext("\tUpdate principal : %s\n"), dbprinc); (void) printf(gettext("\tUpdate size : %u\n"), indx_log->kdb_entry_size); (void) printf(gettext("\tUpdate committed : %s\n"), indx_log->kdb_commit ? "True" : "False"); if (indx_log->kdb_time.seconds == 0L) (void) printf(gettext("\tUpdate time stamp : None\n")); else (void) printf(gettext("\tUpdate time stamp : %s"), ctime((time_t *)&(indx_log->kdb_time.seconds))); (void) printf(gettext("\tAttributes changed : %d\n"), upd.kdb_update.kdbe_t_len); if (verbose) for (j = 0; j < upd.kdb_update.kdbe_t_len; j++) print_attr( upd.kdb_update.kdbe_t_val[j].av_type); xdr_free(xdr_kdb_incr_update_t, (char *)&upd); if (dbprinc) free(dbprinc); } /* for */ } int main(int argc, char **argv) { int c; bool_t verbose = FALSE; bool_t headeronly = FALSE; uint32_t entry = 0; krb5_context context; kadm5_config_params params; kdb_log_context *log_ctx; kdb_hlog_t *ulog = NULL; (void) setlocale(LC_ALL, ""); #if !defined(TEXT_DOMAIN) #define TEXT_DOMAIN "SYS_TEST" #endif /* TEXT_DOMAIN */ (void) textdomain(TEXT_DOMAIN); if (geteuid() != (uid_t)0) { (void) fprintf(stderr, gettext("kproplog must be run as root\n\n")); exit(1); } progname = argv[0]; while ((c = getopt(argc, argv, "vhe:")) != -1) { switch (c) { case 'h': headeronly = TRUE; break; case 'e': entry = atoi(optarg); break; case 'v': verbose = TRUE; break; default: usage(); } } if (krb5_init_context(&context)) { (void) fprintf(stderr, gettext("Unable to initialize Kerberos\n\n")); exit(1); } (void) memset((char *)¶ms, 0, sizeof (params)); if (kadm5_get_config_params(context, NULL, NULL, ¶ms, ¶ms)) { (void) fprintf(stderr, gettext("Couldn't read database_name\n\n")); exit(1); } (void) printf(gettext("\nKerberos update log (%s.ulog)\n"), params.dbname); if (ulog_map(context, ¶ms, FKPROPLOG)) { (void) fprintf(stderr, gettext("Unable to map log file " "%s.ulog\n\n"), params.dbname); exit(1); } log_ctx = context->kdblog_context; if (log_ctx) ulog = log_ctx->ulog; else { (void) fprintf(stderr, gettext("Unable to map log file " "%s.ulog\n\n"), params.dbname); exit(1); } if (ulog->kdb_hmagic != KDB_HMAGIC) { (void) fprintf(stderr, gettext("Corrupt header log, exiting\n\n")); exit(1); } (void) printf(gettext("Update log dump :\n")); (void) printf(gettext("\tLog version # : %u\n"), ulog->db_version_num); (void) printf(gettext("\tLog state : ")); switch (ulog->kdb_state) { case KDB_STABLE: (void) printf(gettext("Stable\n")); break; case KDB_UNSTABLE: (void) printf(gettext("Unstable\n")); break; case KDB_CORRUPT: (void) printf(gettext("Corrupt\n")); break; default: (void) printf(gettext("Unknown state: %d\n"), ulog->kdb_state); break; } (void) printf(gettext("\tEntry block size : %u\n"), ulog->kdb_block); (void) printf(gettext("\tNumber of entries : %u\n"), ulog->kdb_num); if (ulog->kdb_last_sno == 0) (void) printf(gettext("\tLast serial # : None\n")); else { if (ulog->kdb_first_sno == 0) (void) printf(gettext("\tFirst serial # : None\n")); else { (void) printf(gettext("\tFirst serial # : ")); (void) printf("%u\n", ulog->kdb_first_sno); } (void) printf(gettext("\tLast serial # : ")); (void) printf("%u\n", ulog->kdb_last_sno); } if (ulog->kdb_last_time.seconds == 0L) { (void) printf(gettext("\tLast time stamp : None\n")); } else { if (ulog->kdb_first_time.seconds == 0L) (void) printf(gettext("\tFirst time stamp : None\n")); else { (void) printf(gettext("\tFirst time stamp : %s"), ctime((time_t *) &(ulog->kdb_first_time.seconds))); } (void) printf(gettext("\tLast time stamp : %s\n"), ctime((time_t *)&(ulog->kdb_last_time.seconds))); } if ((!headeronly) && ulog->kdb_num) { print_update(ulog, entry, verbose); } (void) printf("\n"); return (0); } # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # PROG= krb5-config SHFILES= krb5-config CLOBBERFILES = $(SHFILES) include ../../Makefile.cmd include $(SRC)/lib/gss_mechs/mech_krb5/Makefile.mech_krb5 .KEEP_STATE: all: $(SHFILES) install: all $(KRB5PROG) clean: $(RM) $(PROG) include ../../Makefile.targ #!/bin/sh # # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # # Copyright 2001, 2002, 2003 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. # # # Configurable parameters set by autoconf version_string="Solaris Kerberos (based on MIT Kerberos 5 release 1.6.3)" prefix=/usr exec_prefix=${prefix} includedir=${prefix}/include/kerberosv5 libdir=${exec_prefix}/lib # Defaults for program library=krb5 # Some constants vendor_string="Sun Microsystems, Inc." # Process arguments # Yes, we are sloppy, library specifications can come before options while test $# != 0; do case $1 in --all) do_all=1 ;; --cflags) do_cflags=1 ;; --deps) do_deps=1 ;; --exec-prefix) do_exec_prefix=1 ;; --help) do_help=1 ;; --libs) do_libs=1 ;; --prefix) do_prefix=1 ;; --vendor) do_vendor=1 ;; --version) do_version=1 ;; krb5) library=krb5 ;; gssapi) library=gssapi ;; *) echo "$0: Unknown option \`$1' -- use \`--help' for usage" exit 1 esac shift done # If required options - provide help if test -z "$do_all" -a -z "$do_version" -a -z "$do_vendor" -a -z "$do_prefix" -a -z "$do_vendor" -a -z "$do_exec_prefix" -a -z "$do_cflags" -a -z "$do_libs"; then do_help=1 fi if test -n "$do_help"; then echo "Usage: $0 [OPTIONS] [LIBRARIES]" echo "Options:" echo " [--help] Help" echo " [--all] Display version, vendor, and various values" echo " [--version] Version information" echo " [--vendor] Vendor information" echo " [--prefix] Kerberos installed prefix" echo " [--exec-prefix] Kerberos installed exec_prefix" echo " [--cflags] Compile time CFLAGS" echo " [--libs] List libraries required to link [LIBRARIES]" echo "Libraries:" echo " krb5 Kerberos 5 application" echo " gssapi GSSAPI application" exit 0 fi if test -n "$do_all"; then all_exit= do_version=1 do_prefix=1 do_exec_prefix=1 do_vendor=1 title_version="Version: " title_prefix="Prefix: " title_exec_prefix="Exec_prefix: " title_vendor="Vendor: " else all_exit="exit 0" fi if test -n "$do_version"; then echo "$title_version$version_string" $all_exit fi if test -n "$do_vendor"; then echo "$title_vendor$vendor_string" $all_exit fi if test -n "$do_prefix"; then echo "$title_prefix$prefix" $all_exit fi if test -n "$do_exec_prefix"; then echo "$title_exec_prefix$exec_prefix" $all_exit fi if test -n "$do_cflags"; then echo "-I${includedir}" fi if test -n "$do_libs"; then lib_flags="-L$libdir" if test $library = 'gssapi'; then lib_flags="$lib_flags -lgss" library=krb5 fi if test $library = 'krb5'; then lib_flags="$lib_flags -lkrb5" fi echo "$lib_flags" fi exit 0 # # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Copyright (c) 2018, Joyent, Inc. PROG= krb5kdc MANIFEST= krb5kdc.xml OBJS = \ dispatch.o\ do_as_req.o\ do_tgs_req.o\ extern.o\ kdc_preauth.o\ kdc_util.o\ main.o\ network.o\ policy.o\ replay.o \ sock2p.o SRCS = $(OBJS:.o=.c) DEFS = -DHAVE_LIBSOCKET=1 -DHAVE_LIBNSL=1 \ -DHAVE_SYSLOG_H=1 -DHAVE_STDARG_H=1 \ -DHAVE_SYS_SELECT_H=1 -DHAVE_OPENLOG=1 \ -DHAVE_SYSLOG=1 -DHAVE_CLOSELOG=1 -DHAVE_STRFTIME=1\ -DHAVE_VSPRINTF=1 -DHAVE_COMPILE=1 -DHAVE_STEP=1 \ -DHAVE_NETINET_IN_H=1 -DHAVE_INET_NTOP=1 \ -DHAVE_SYS_SOCKIO_H=1 -DHAVE_SYS_SELECT_H=1 CLOBBERFILES += $(RSRC) include ../../Makefile.cmd include $(SRC)/lib/gss_mechs/mech_krb5/Makefile.mech_krb5 POFILE = $(PROG).po POFILES = generic.po ROOTMANIFESTDIR= $(ROOTSVCNETWORKSECURITY) CFLAGS += $(CCOPTS) $(DEFS) $(LOCALINCLUDE) # The SET macros in network.c will be mis-optimized if GCC believes it # may rely on undefined signed overflow behaviour. CFLAGS += -fno-strict-overflow CPPFLAGS += \ -I$(SRC)/lib/gss_mechs/mech_krb5/include \ -I$(SRC)/lib/gss_mechs/mech_krb5/include/krb5\ -I$(SRC)/uts/common/gssapi/mechs/krb5/include \ -DHAVE_SYSLOG_H=1 CERRWARN += -Wno-unused-variable CERRWARN += -Wno-unused-function CERRWARN += -Wno-type-limits CERRWARN += -Wno-implicit-function-declaration CERRWARN += -Wno-parentheses # not linted SMATCH=off LDFLAGS += $(KRUNPATH) $(KERBRUNPATH) # Hammerhead: GNU ld needs rpath-link for transitive deps (libdyn, libkdb) via libkadm5srv LDFLAGS += -Wl,-rpath-link,$(ROOT)/usr/lib/krb5 LDLIBS += -L$(ROOT_KLIBDIR) -L$(KRB5LIB) \ -lmech_krb5 -lkdb -lkadm5srv -lnsl -lsocket -lbsm -lc .KEEP_STATE: all: $(PROG) $(PROG): $(OBJS) $(LINK.c) $(OBJS) -o $@ $(LDLIBS) $(POST_PROCESS) $(KRB5LIBPROG): FILEMODE = 500 install: $(KRB5LIBPROG) $(ROOTSVCMETHOD) $(ROOTMANIFEST) check: $(CHKMANIFEST) clean: $(RM) $(OBJS) include ../../Makefile.targ $(POFILE): $(DERIVED_FILES) .WAIT $(POFILES) $(RM) $@ $(CAT) $(POFILES) > $@ generic.po: FRC $(RM) messages.po $(XGETTEXT) $(XGETFLAGS) `$(GREP) -l gettext *.[ch]` $(SED) "/^domain/d" messages.po > $@ $(RM) messages.po FRC: /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * kdc/dispatch.c * * Copyright 1990 by the Massachusetts Institute of Technology. * * 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. * * * Dispatch an incoming packet. */ #include "k5-int.h" #include #include "kdc_util.h" #include "extern.h" #include "adm_proto.h" #include #include #include static krb5_int32 last_usec = 0, last_os_random = 0; krb5_error_code dispatch(krb5_data *pkt, const krb5_fulladdr *from, krb5_data **response) { krb5_error_code retval; krb5_kdc_req *as_req; krb5_int32 now, now_usec; /* decode incoming packet, and dispatch */ #ifndef NOCACHE /* try the replay lookaside buffer */ if (kdc_check_lookaside(pkt, response)) { /* a hit! */ const char *name = 0; char buf[46]; name = (char *) inet_ntop (ADDRTYPE2FAMILY (from->address->addrtype), from->address->contents, buf, sizeof (buf)); if (name == 0) name = "[unknown address type]"; krb5_klog_syslog(LOG_INFO, "DISPATCH: repeated (retransmitted?) request from %s, resending previous response", name); return 0; } #endif /* SUNW14resync XXX */ #if 0 retval = krb5_crypto_us_timeofday(&now, &now_usec); if (retval == 0) { krb5_int32 usec_difference = now_usec-last_usec; krb5_data data; if(last_os_random == 0) last_os_random = now; /* Grab random data from OS every hour*/ if(now-last_os_random >= 60*60) { krb5_c_random_os_entropy(kdc_context, 0, NULL); last_os_random = now; } data.length = sizeof(krb5_int32); data.data = (void *) &usec_difference; krb5_c_random_add_entropy(kdc_context, KRB5_C_RANDSOURCE_TIMING, &data); last_usec = now_usec; } #endif /* try TGS_REQ first; they are more common! */ if (krb5_is_tgs_req(pkt)) { retval = process_tgs_req(pkt, from, response); } else if (krb5_is_as_req(pkt)) { if (!(retval = decode_krb5_as_req(pkt, &as_req))) { /* * setup_server_realm() sets up the global realm-specific data * pointer. */ if (!(retval = setup_server_realm(as_req->server))) { retval = process_as_req(as_req, pkt, from, response); } krb5_free_kdc_req(kdc_context, as_req); } } #ifdef KRB5_KRB4_COMPAT else if (pkt->data[0] == 4) /* old version */ retval = process_v4(pkt, from, response); #endif else retval = KRB5KRB_AP_ERR_MSG_TYPE; #ifndef NOCACHE /* put the response into the lookaside buffer */ if (!retval) kdc_insert_lookaside(pkt, *response); #endif return retval; } /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * kdc/do_as_req.c * * Copyright 1990,1991 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. * * * KDC Routines to deal with AS_REQ's */ #define NEED_SOCKETS #include "k5-int.h" #include "com_err.h" #include #ifdef HAVE_NETINET_IN_H #include #include #ifndef hpux #include #endif /* hpux */ #endif /* HAVE_NETINET_IN_H */ #include "kdc_util.h" #include "policy.h" #include "adm.h" #include "adm_proto.h" #include "extern.h" static krb5_error_code prepare_error_as (krb5_kdc_req *, int, krb5_data *, krb5_data **, const char *); /*ARGSUSED*/ krb5_error_code process_as_req(krb5_kdc_req *request, krb5_data *req_pkt, const krb5_fulladdr *from, krb5_data **response) { krb5_db_entry client, server; krb5_kdc_rep reply; krb5_enc_kdc_rep_part reply_encpart; krb5_ticket ticket_reply; krb5_enc_tkt_part enc_tkt_reply; krb5_error_code errcode; int c_nprincs = 0, s_nprincs = 0; krb5_boolean more; krb5_timestamp kdc_time, authtime, etime = 0; krb5_keyblock session_key; krb5_keyblock encrypting_key; const char *status; krb5_key_data *server_key, *client_key; krb5_enctype useenctype; #ifdef KRBCONF_KDC_MODIFIES_KDB krb5_boolean update_client = 0; #endif /* KRBCONF_KDC_MODIFIES_KDB */ krb5_data e_data; register int i; krb5_timestamp until, rtime; long long tmp_client_times, tmp_server_times, tmp_realm_times; char *cname = 0, *sname = 0; const char *fromstring = 0; char ktypestr[128]; char rep_etypestr[128]; char fromstringbuf[70]; void *pa_context = NULL; struct in_addr from_in4; /* IPv4 address of sender */ ticket_reply.enc_part.ciphertext.data = 0; e_data.data = 0; (void) memset(&encrypting_key, 0, sizeof(krb5_keyblock)); reply.padata = 0; /* avoid bogus free in error_out */ (void) memset(&session_key, 0, sizeof(krb5_keyblock)); enc_tkt_reply.authorization_data = NULL; ktypes2str(ktypestr, sizeof(ktypestr), request->nktypes, request->ktype); (void) memcpy(&from_in4, from->address->contents, /* SUNW */ sizeof (struct in_addr)); fromstring = inet_ntop(ADDRTYPE2FAMILY (from->address->addrtype), &from_in4, fromstringbuf, sizeof(fromstringbuf)); if (!fromstring) fromstring = ""; if (!request->client) { status = "NULL_CLIENT"; errcode = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; goto errout; } if ((errcode = krb5_unparse_name(kdc_context, request->client, &cname))) { status = "UNPARSING_CLIENT"; goto errout; } limit_string(cname); if (!request->server) { status = "NULL_SERVER"; errcode = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; goto errout; } if ((errcode = krb5_unparse_name(kdc_context, request->server, &sname))) { status = "UNPARSING_SERVER"; goto errout; } limit_string(sname); c_nprincs = 1; if ((errcode = krb5_db_get_principal(kdc_context, request->client, &client, &c_nprincs, &more))) { status = "LOOKING_UP_CLIENT"; c_nprincs = 0; goto errout; } if (more) { status = "NON-UNIQUE_CLIENT"; errcode = KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE; goto errout; } else if (c_nprincs != 1) { status = "CLIENT_NOT_FOUND"; #ifdef KRBCONF_VAGUE_ERRORS errcode = KRB5KRB_ERR_GENERIC; #else errcode = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; #endif goto errout; } s_nprincs = 1; if ((errcode = krb5_db_get_principal(kdc_context, request->server, &server, &s_nprincs, &more))) { status = "LOOKING_UP_SERVER"; goto errout; } if (more) { status = "NON-UNIQUE_SERVER"; errcode = KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE; goto errout; } else if (s_nprincs != 1) { status = "SERVER_NOT_FOUND"; errcode = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; goto errout; } if ((errcode = krb5_timeofday(kdc_context, &kdc_time))) { status = "TIMEOFDAY"; goto errout; } if ((errcode = validate_as_request(request, client, server, kdc_time, &status))) { if (!status) status = "UNKNOWN_REASON"; errcode += ERROR_TABLE_BASE_krb5; goto errout; } /* * Select the keytype for the ticket session key. */ if ((useenctype = select_session_keytype(kdc_context, &server, request->nktypes, request->ktype)) == 0) { /* unsupported ktype */ status = "BAD_ENCRYPTION_TYPE"; errcode = KRB5KDC_ERR_ETYPE_NOSUPP; goto errout; } if ((errcode = krb5_c_make_random_key(kdc_context, useenctype, &session_key))) { /* random key failed */ status = "RANDOM_KEY_FAILED"; goto errout; } ticket_reply.server = request->server; enc_tkt_reply.flags = 0; setflag(enc_tkt_reply.flags, TKT_FLG_INITIAL); /* It should be noted that local policy may affect the */ /* processing of any of these flags. For example, some */ /* realms may refuse to issue renewable tickets */ if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE)) setflag(enc_tkt_reply.flags, TKT_FLG_FORWARDABLE); if (isflagset(request->kdc_options, KDC_OPT_PROXIABLE)) setflag(enc_tkt_reply.flags, TKT_FLG_PROXIABLE); if (isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE)) setflag(enc_tkt_reply.flags, TKT_FLG_MAY_POSTDATE); enc_tkt_reply.session = &session_key; enc_tkt_reply.client = request->client; enc_tkt_reply.transited.tr_type = KRB5_DOMAIN_X500_COMPRESS; enc_tkt_reply.transited.tr_contents = empty_string; /* equivalent of "" */ enc_tkt_reply.times.authtime = kdc_time; if (isflagset(request->kdc_options, KDC_OPT_POSTDATED)) { setflag(enc_tkt_reply.flags, TKT_FLG_POSTDATED); setflag(enc_tkt_reply.flags, TKT_FLG_INVALID); enc_tkt_reply.times.starttime = request->from; } else enc_tkt_reply.times.starttime = kdc_time; until = (request->till == 0) ? kdc_infinity : request->till; /* These numbers could easily be large * use long long variables to ensure that they don't * result in negative values when added. */ tmp_client_times = (long long) enc_tkt_reply.times.starttime + client.max_life; tmp_server_times = (long long) enc_tkt_reply.times.starttime + server.max_life; tmp_realm_times = (long long) enc_tkt_reply.times.starttime + max_life_for_realm; enc_tkt_reply.times.endtime = min(until, min(tmp_client_times, min(tmp_server_times, min(tmp_realm_times,KRB5_KDB_EXPIRATION)))); if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE_OK) && !isflagset(client.attributes, KRB5_KDB_DISALLOW_RENEWABLE) && (enc_tkt_reply.times.endtime < request->till)) { /* we set the RENEWABLE option for later processing */ setflag(request->kdc_options, KDC_OPT_RENEWABLE); request->rtime = request->till; } rtime = (request->rtime == 0) ? kdc_infinity : request->rtime; if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE)) { /* * XXX Should we squelch the output renew_till to be no * earlier than the endtime of the ticket? */ setflag(enc_tkt_reply.flags, TKT_FLG_RENEWABLE); tmp_client_times = (double) enc_tkt_reply.times.starttime + client.max_renewable_life; tmp_server_times = (double) enc_tkt_reply.times.starttime + server.max_renewable_life; tmp_realm_times = (double) enc_tkt_reply.times.starttime + max_renewable_life_for_realm; enc_tkt_reply.times.renew_till = min(rtime, min(tmp_client_times, min(tmp_server_times, min(tmp_realm_times,KRB5_KDB_EXPIRATION)))); } else enc_tkt_reply.times.renew_till = 0; /* XXX */ /* starttime is optional, and treated as authtime if not present. so we can nuke it if it matches */ if (enc_tkt_reply.times.starttime == enc_tkt_reply.times.authtime) enc_tkt_reply.times.starttime = 0; enc_tkt_reply.caddrs = request->addresses; enc_tkt_reply.authorization_data = 0; /* * Check the preauthentication if it is there. */ if (request->padata) { errcode = check_padata(kdc_context, &client, req_pkt, request, &enc_tkt_reply, &pa_context, &e_data); if (errcode) { #ifdef KRBCONF_KDC_MODIFIES_KDB /* * Note: this doesn't work if you're using slave servers!!! * It also causes the database to be modified (and thus * need to be locked) frequently. */ if (client.fail_auth_count < KRB5_MAX_FAIL_COUNT) { client.fail_auth_count = client.fail_auth_count + 1; if (client.fail_auth_count == KRB5_MAX_FAIL_COUNT) { client.attributes |= KRB5_KDB_DISALLOW_ALL_TIX; } } client.last_failed = kdc_time; update_client = 1; #endif status = "PREAUTH_FAILED"; #ifdef KRBCONF_VAGUE_ERRORS errcode = KRB5KRB_ERR_GENERIC; #endif goto errout; } } /* * Final check before handing out ticket: If the client requires * preauthentication, verify that the proper kind of * preauthentication was carried out. */ status = missing_required_preauth(&client, &server, &enc_tkt_reply); if (status) { errcode = KRB5KDC_ERR_PREAUTH_REQUIRED; get_preauth_hint_list(request, &client, &server, &e_data); goto errout; } ticket_reply.enc_part2 = &enc_tkt_reply; /* * Find the server key */ if ((errcode = krb5_dbe_find_enctype(kdc_context, &server, -1, /* ignore keytype */ -1, /* Ignore salttype */ 0, /* Get highest kvno */ &server_key))) { status = "FINDING_SERVER_KEY"; goto errout; } /* convert server.key into a real key (it may be encrypted in the database) */ if ((errcode = krb5_dbekd_decrypt_key_data(kdc_context, &master_keyblock, server_key, &encrypting_key, NULL))) { status = "DECRYPT_SERVER_KEY"; goto errout; } errcode = krb5_encrypt_tkt_part(kdc_context, &encrypting_key, &ticket_reply); krb5_free_keyblock_contents(kdc_context, &encrypting_key); encrypting_key.contents = 0; if (errcode) { status = "ENCRYPTING_TICKET"; goto errout; } ticket_reply.enc_part.kvno = server_key->key_data_kvno; /* * Find the appropriate client key. We search in the order specified * by request keytype list. */ client_key = (krb5_key_data *) NULL; for (i = 0; i < request->nktypes; i++) { useenctype = request->ktype[i]; if (!krb5_c_valid_enctype(useenctype)) continue; if (!krb5_dbe_find_enctype(kdc_context, &client, useenctype, -1, 0, &client_key)) break; } if (!(client_key)) { /* Cannot find an appropriate key */ status = "CANT_FIND_CLIENT_KEY"; errcode = KRB5KDC_ERR_ETYPE_NOSUPP; goto errout; } /* convert client.key_data into a real key */ if ((errcode = krb5_dbekd_decrypt_key_data(kdc_context, &master_keyblock, client_key, &encrypting_key, NULL))) { status = "DECRYPT_CLIENT_KEY"; goto errout; } encrypting_key.enctype = useenctype; /* Start assembling the response */ reply.msg_type = KRB5_AS_REP; reply.client = request->client; reply.ticket = &ticket_reply; reply_encpart.session = &session_key; if ((errcode = fetch_last_req_info(&client, &reply_encpart.last_req))) { status = "FETCH_LAST_REQ"; goto errout; } reply_encpart.nonce = request->nonce; /* * Take the minimum of expiration or pw_expiration if not zero. */ if (client.expiration != 0 && client.pw_expiration != 0) etime = min(client.expiration, client.pw_expiration); else etime = client.expiration ? client.expiration : client.pw_expiration; reply_encpart.key_exp = etime; reply_encpart.flags = enc_tkt_reply.flags; reply_encpart.server = ticket_reply.server; /* copy the time fields EXCEPT for authtime; it's location is used for ktime */ reply_encpart.times = enc_tkt_reply.times; reply_encpart.times.authtime = authtime = kdc_time; reply_encpart.caddrs = enc_tkt_reply.caddrs; /* Fetch the padata info to be returned */ errcode = return_padata(kdc_context, &client, req_pkt, request, &reply, client_key, &encrypting_key, &pa_context); if (errcode) { status = "KDC_RETURN_PADATA"; goto errout; } /* now encode/encrypt the response */ reply.enc_part.enctype = encrypting_key.enctype; errcode = krb5_encode_kdc_rep(kdc_context, KRB5_AS_REP, &reply_encpart, 0, &encrypting_key, &reply, response); krb5_free_keyblock_contents(kdc_context, &encrypting_key); encrypting_key.contents = 0; reply.enc_part.kvno = client_key->key_data_kvno; if (errcode) { status = "ENCODE_KDC_REP"; goto errout; } /* these parts are left on as a courtesy from krb5_encode_kdc_rep so we can use them in raw form if needed. But, we don't... */ memset(reply.enc_part.ciphertext.data, 0, reply.enc_part.ciphertext.length); free(reply.enc_part.ciphertext.data); /* SUNW14resync: * The third argument to audit_krb5kdc_as_req() is zero as the local * portnumber is no longer passed to process_as_req(). */ audit_krb5kdc_as_req(&from_in4, (in_port_t)from->port, 0, cname, sname, 0); rep_etypes2str(rep_etypestr, sizeof(rep_etypestr), &reply); krb5_klog_syslog(LOG_INFO, "AS_REQ (%s) %s: ISSUE: authtime %d, " "%s, %s for %s", ktypestr, fromstring, authtime, rep_etypestr, cname, sname); #ifdef KRBCONF_KDC_MODIFIES_KDB /* * If we get this far, we successfully did the AS_REQ. */ client.last_success = kdc_time; client.fail_auth_count = 0; update_client = 1; #endif /* KRBCONF_KDC_MODIFIES_KDB */ errout: if (pa_context) free_padata_context(kdc_context, &pa_context); if (status) { const char *emsg = NULL; if (errcode) emsg = krb5_get_error_message (kdc_context, errcode); audit_krb5kdc_as_req(&from_in4, (in_port_t)from->port, 0, cname, sname, errcode); krb5_klog_syslog(LOG_INFO, "AS_REQ (%s) %s: %s: %s for %s%s%s", ktypestr, fromstring, status, cname ? cname : "", sname ? sname : "", errcode ? ", " : "", errcode ? emsg : ""); if (errcode) krb5_free_error_message (kdc_context, emsg); } if (errcode) { int got_err = 0; if (status == 0) { status = krb5_get_error_message (kdc_context, errcode); got_err = 1; } errcode -= ERROR_TABLE_BASE_krb5; if (errcode < 0 || errcode > 128) errcode = KRB_ERR_GENERIC; errcode = prepare_error_as(request, errcode, &e_data, response, status); if (got_err) { krb5_free_error_message (kdc_context, status); status = 0; } } if (enc_tkt_reply.authorization_data != NULL) krb5_free_authdata(kdc_context, enc_tkt_reply.authorization_data); if (encrypting_key.contents) krb5_free_keyblock_contents(kdc_context, &encrypting_key); if (reply.padata) krb5_free_pa_data(kdc_context, reply.padata); if (cname) free(cname); if (sname) free(sname); if (c_nprincs) { #ifdef KRBCONF_KDC_MODIFIES_KDB if (update_client) { krb5_db_put_principal(kdc_context, &client, &c_nprincs); /* * ptooey. We want krb5_db_sync() or something like that. */ krb5_db_fini(kdc_context); if (kdc_active_realm->realm_dbname) krb5_db_set_name(kdc_active_realm->realm_context, kdc_active_realm->realm_dbname); krb5_db_init(kdc_context); /* Reset master key */ krb5_db_set_mkey(kdc_context, &kdc_active_realm->realm_mkey); } #endif /* KRBCONF_KDC_MODIFIES_KDB */ krb5_db_free_principal(kdc_context, &client, c_nprincs); } if (s_nprincs) krb5_db_free_principal(kdc_context, &server, s_nprincs); if (session_key.contents) krb5_free_keyblock_contents(kdc_context, &session_key); if (ticket_reply.enc_part.ciphertext.data) { memset(ticket_reply.enc_part.ciphertext.data , 0, ticket_reply.enc_part.ciphertext.length); free(ticket_reply.enc_part.ciphertext.data); } krb5_free_data_contents(kdc_context, &e_data); return errcode; } static krb5_error_code prepare_error_as (krb5_kdc_req *request, int error, krb5_data *e_data, krb5_data **response, const char *status) { krb5_error errpkt; krb5_error_code retval; krb5_data *scratch; errpkt.ctime = request->nonce; errpkt.cusec = 0; if ((retval = krb5_us_timeofday(kdc_context, &errpkt.stime, &errpkt.susec))) return(retval); errpkt.error = error; errpkt.server = request->server; errpkt.client = request->client; errpkt.text.length = strlen(status)+1; if (!(errpkt.text.data = malloc(errpkt.text.length))) return ENOMEM; (void) strcpy(errpkt.text.data, status); if (!(scratch = (krb5_data *)malloc(sizeof(*scratch)))) { free(errpkt.text.data); return ENOMEM; } if (e_data && e_data->data) { errpkt.e_data = *e_data; } else { errpkt.e_data.length = 0; errpkt.e_data.data = 0; } retval = krb5_mk_error(kdc_context, &errpkt, scratch); free(errpkt.text.data); if (retval) free(scratch); else *response = scratch; return retval; } /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * kdc/do_tgs_req.c * * Copyright 1990,1991,2001 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. * * * KDC Routines to deal with TGS_REQ's */ #include "k5-int.h" #include "com_err.h" #include #ifdef HAVE_NETINET_IN_H #include #include #ifndef hpux #include #endif #endif #include "kdc_util.h" #include "policy.h" #include "extern.h" #include "adm_proto.h" extern krb5_error_code setup_server_realm(krb5_principal); static void find_alternate_tgs (krb5_kdc_req *, krb5_db_entry *, krb5_boolean *, int *, const krb5_fulladdr *from, char *cname); static krb5_error_code prepare_error_tgs (krb5_kdc_req *, krb5_ticket *, int, const char *, krb5_data **, const char *); /*ARGSUSED*/ krb5_error_code process_tgs_req(krb5_data *pkt, const krb5_fulladdr *from, krb5_data **response) { krb5_keyblock * subkey; krb5_kdc_req *request = 0; krb5_db_entry server; krb5_kdc_rep reply; krb5_enc_kdc_rep_part reply_encpart; krb5_ticket ticket_reply, *header_ticket = 0; int st_idx = 0; krb5_enc_tkt_part enc_tkt_reply; krb5_transited enc_tkt_transited; int newtransited = 0; krb5_error_code retval = 0; int nprincs = 0; krb5_boolean more; krb5_timestamp kdc_time, authtime=0; krb5_keyblock session_key; krb5_timestamp until, rtime; krb5_keyblock encrypting_key; krb5_key_data *server_key; char *cname = 0, *sname = 0, *tmp = 0; const char *fromstring = 0; krb5_last_req_entry *nolrarray[2], nolrentry; /* krb5_address *noaddrarray[1]; */ krb5_enctype useenctype; int errcode, errcode2; register int i; int firstpass = 1; const char *status = 0; char ktypestr[128]; char rep_etypestr[128]; char fromstringbuf[70]; long long tmp_server_times, tmp_realm_times; (void) memset(&encrypting_key, 0, sizeof(krb5_keyblock)); (void) memset(&session_key, 0, sizeof(krb5_keyblock)); retval = decode_krb5_tgs_req(pkt, &request); if (retval) return retval; ktypes2str(ktypestr, sizeof(ktypestr), request->nktypes, request->ktype); /* * setup_server_realm() sets up the global realm-specific data pointer. */ if ((retval = setup_server_realm(request->server))) return retval; fromstring = inet_ntop(ADDRTYPE2FAMILY(from->address->addrtype), from->address->contents, fromstringbuf, sizeof(fromstringbuf)); if (!fromstring) fromstring = ""; if ((errcode = krb5_unparse_name(kdc_context, request->server, &sname))) { status = "UNPARSING SERVER"; goto cleanup; } limit_string(sname); /* errcode = kdc_process_tgs_req(request, from, pkt, &req_authdat); */ errcode = kdc_process_tgs_req(request, from, pkt, &header_ticket, &subkey); if (header_ticket && header_ticket->enc_part2 && (errcode2 = krb5_unparse_name(kdc_context, header_ticket->enc_part2->client, &cname))) { status = "UNPARSING CLIENT"; errcode = errcode2; goto cleanup; } limit_string(cname); if (errcode) { status = "PROCESS_TGS"; goto cleanup; } if (!header_ticket) { errcode = KRB5_NO_TKT_SUPPLIED; /* XXX? */ status="UNEXPECTED NULL in header_ticket"; goto cleanup; } /* * We've already dealt with the AP_REQ authentication, so we can * use header_ticket freely. The encrypted part (if any) has been * decrypted with the session key. */ authtime = header_ticket->enc_part2->times.authtime; /* XXX make sure server here has the proper realm...taken from AP_REQ header? */ nprincs = 1; if ((errcode = krb5_db_get_principal(kdc_context, request->server, &server, &nprincs, &more))) { status = "LOOKING_UP_SERVER"; nprincs = 0; goto cleanup; } tgt_again: if (more) { status = "NON_UNIQUE_PRINCIPAL"; errcode = KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE; goto cleanup; } else if (nprincs != 1) { /* * might be a request for a TGT for some other realm; we * should do our best to find such a TGS in this db */ if (firstpass && krb5_is_tgs_principal(request->server) == TRUE) { if (krb5_princ_size(kdc_context, request->server) == 2) { krb5_data *server_1 = krb5_princ_component(kdc_context, request->server, 1); krb5_data *tgs_1 = krb5_princ_component(kdc_context, tgs_server, 1); if (!tgs_1 || server_1->length != tgs_1->length || memcmp(server_1->data, tgs_1->data, tgs_1->length)) { krb5_db_free_principal(kdc_context, &server, nprincs); find_alternate_tgs(request, &server, &more, &nprincs, from, cname); firstpass = 0; goto tgt_again; } } } krb5_db_free_principal(kdc_context, &server, nprincs); status = "UNKNOWN_SERVER"; errcode = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; goto cleanup; } if ((errcode = krb5_timeofday(kdc_context, &kdc_time))) { status = "TIME_OF_DAY"; goto cleanup; } if ((retval = validate_tgs_request(request, server, header_ticket, kdc_time, &status))) { if (!status) status = "UNKNOWN_REASON"; errcode = retval + ERROR_TABLE_BASE_krb5; goto cleanup; } /* * We pick the session keytype here.... * * Some special care needs to be taken in the user-to-user * case, since we don't know what keytypes the application server * which is doing user-to-user authentication can support. We * know that it at least must be able to support the encryption * type of the session key in the TGT, since otherwise it won't be * able to decrypt the U2U ticket! So we use that in preference * to anything else. */ useenctype = 0; if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY)) { krb5_keyblock * st_sealing_key; krb5_kvno st_srv_kvno; krb5_enctype etype; /* * Get the key for the second ticket, and decrypt it. */ if ((errcode = kdc_get_server_key(request->second_ticket[st_idx], &st_sealing_key, &st_srv_kvno))) { status = "2ND_TKT_SERVER"; goto cleanup; } errcode = krb5_decrypt_tkt_part(kdc_context, st_sealing_key, request->second_ticket[st_idx]); krb5_free_keyblock(kdc_context, st_sealing_key); if (errcode) { status = "2ND_TKT_DECRYPT"; goto cleanup; } etype = request->second_ticket[st_idx]->enc_part2->session->enctype; if (!krb5_c_valid_enctype(etype)) { status = "BAD_ETYPE_IN_2ND_TKT"; errcode = KRB5KDC_ERR_ETYPE_NOSUPP; goto cleanup; } for (i = 0; i < request->nktypes; i++) { if (request->ktype[i] == etype) { useenctype = etype; break; } } } /* * Select the keytype for the ticket session key. */ if ((useenctype == 0) && (useenctype = select_session_keytype(kdc_context, &server, request->nktypes, request->ktype)) == 0) { /* unsupported ktype */ status = "BAD_ENCRYPTION_TYPE"; errcode = KRB5KDC_ERR_ETYPE_NOSUPP; goto cleanup; } errcode = krb5_c_make_random_key(kdc_context, useenctype, &session_key); if (errcode) { /* random key failed */ status = "RANDOM_KEY_FAILED"; goto cleanup; } ticket_reply.server = request->server; /* XXX careful for realm... */ enc_tkt_reply.flags = 0; enc_tkt_reply.times.starttime = 0; /* * Fix header_ticket's starttime; if it's zero, fill in the * authtime's value. */ if (!(header_ticket->enc_part2->times.starttime)) header_ticket->enc_part2->times.starttime = header_ticket->enc_part2->times.authtime; /* don't use new addresses unless forwarded, see below */ enc_tkt_reply.caddrs = header_ticket->enc_part2->caddrs; /* noaddrarray[0] = 0; */ reply_encpart.caddrs = 0; /* optional...don't put it in */ /* It should be noted that local policy may affect the */ /* processing of any of these flags. For example, some */ /* realms may refuse to issue renewable tickets */ if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE)) setflag(enc_tkt_reply.flags, TKT_FLG_FORWARDABLE); if (isflagset(request->kdc_options, KDC_OPT_FORWARDED)) { setflag(enc_tkt_reply.flags, TKT_FLG_FORWARDED); /* include new addresses in ticket & reply */ enc_tkt_reply.caddrs = request->addresses; reply_encpart.caddrs = request->addresses; } if (isflagset(header_ticket->enc_part2->flags, TKT_FLG_FORWARDED)) setflag(enc_tkt_reply.flags, TKT_FLG_FORWARDED); if (isflagset(request->kdc_options, KDC_OPT_PROXIABLE)) setflag(enc_tkt_reply.flags, TKT_FLG_PROXIABLE); if (isflagset(request->kdc_options, KDC_OPT_PROXY)) { setflag(enc_tkt_reply.flags, TKT_FLG_PROXY); /* include new addresses in ticket & reply */ enc_tkt_reply.caddrs = request->addresses; reply_encpart.caddrs = request->addresses; } if (isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE)) setflag(enc_tkt_reply.flags, TKT_FLG_MAY_POSTDATE); if (isflagset(request->kdc_options, KDC_OPT_POSTDATED)) { setflag(enc_tkt_reply.flags, TKT_FLG_POSTDATED); setflag(enc_tkt_reply.flags, TKT_FLG_INVALID); enc_tkt_reply.times.starttime = request->from; } else enc_tkt_reply.times.starttime = kdc_time; if (isflagset(request->kdc_options, KDC_OPT_VALIDATE)) { /* BEWARE of allocation hanging off of ticket & enc_part2, it belongs to the caller */ ticket_reply = *(header_ticket); enc_tkt_reply = *(header_ticket->enc_part2); clear(enc_tkt_reply.flags, TKT_FLG_INVALID); } if (isflagset(request->kdc_options, KDC_OPT_RENEW)) { krb5_deltat old_life; /* BEWARE of allocation hanging off of ticket & enc_part2, it belongs to the caller */ ticket_reply = *(header_ticket); enc_tkt_reply = *(header_ticket->enc_part2); old_life = enc_tkt_reply.times.endtime - enc_tkt_reply.times.starttime; enc_tkt_reply.times.starttime = kdc_time; enc_tkt_reply.times.endtime = min(header_ticket->enc_part2->times.renew_till, kdc_time + old_life); } else { /* not a renew request */ enc_tkt_reply.times.starttime = kdc_time; until = (request->till == 0) ? kdc_infinity : request->till; /* SUNW */ tmp_server_times = (long long) enc_tkt_reply.times.starttime + server.max_life; tmp_realm_times = (long long) enc_tkt_reply.times.starttime + max_life_for_realm; enc_tkt_reply.times.endtime = min(until, min(tmp_server_times, min(tmp_realm_times, min(header_ticket->enc_part2->times.endtime, KRB5_KDB_EXPIRATION)))); /* SUNW */ /* enc_tkt_reply.times.endtime = min(until, min(enc_tkt_reply.times.starttime + server.max_life, min(enc_tkt_reply.times.starttime + max_life_for_realm, min(header_ticket->enc_part2->times.endtime))); */ if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE_OK) && (enc_tkt_reply.times.endtime < request->till) && isflagset(header_ticket->enc_part2->flags, TKT_FLG_RENEWABLE)) { setflag(request->kdc_options, KDC_OPT_RENEWABLE); request->rtime = min(request->till, min(KRB5_KDB_EXPIRATION, header_ticket->enc_part2->times.renew_till)); } } rtime = (request->rtime == 0) ? kdc_infinity : request->rtime; if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE)) { /* already checked above in policy check to reject request for a renewable ticket using a non-renewable ticket */ setflag(enc_tkt_reply.flags, TKT_FLG_RENEWABLE); tmp_realm_times = (long long) enc_tkt_reply.times.starttime + min(server.max_renewable_life,max_renewable_life_for_realm); enc_tkt_reply.times.renew_till = min(rtime, min(header_ticket->enc_part2->times.renew_till, min (tmp_realm_times, KRB5_KDB_EXPIRATION))); } else { enc_tkt_reply.times.renew_till = 0; } /* * Set authtime to be the same as header_ticket's */ enc_tkt_reply.times.authtime = header_ticket->enc_part2->times.authtime; /* * Propagate the preauthentication flags through to the returned ticket. */ if (isflagset(header_ticket->enc_part2->flags, TKT_FLG_PRE_AUTH)) setflag(enc_tkt_reply.flags, TKT_FLG_PRE_AUTH); if (isflagset(header_ticket->enc_part2->flags, TKT_FLG_HW_AUTH)) setflag(enc_tkt_reply.flags, TKT_FLG_HW_AUTH); /* starttime is optional, and treated as authtime if not present. so we can nuke it if it matches */ if (enc_tkt_reply.times.starttime == enc_tkt_reply.times.authtime) enc_tkt_reply.times.starttime = 0; /* assemble any authorization data */ if (request->authorization_data.ciphertext.data) { krb5_data scratch; scratch.length = request->authorization_data.ciphertext.length; if (!(scratch.data = malloc(request->authorization_data.ciphertext.length))) { status = "AUTH_NOMEM"; errcode = ENOMEM; goto cleanup; } if ((errcode = krb5_c_decrypt(kdc_context, header_ticket->enc_part2->session, KRB5_KEYUSAGE_TGS_REQ_AD_SESSKEY, 0, &request->authorization_data, &scratch))) { status = "AUTH_ENCRYPT_FAIL"; free(scratch.data); goto cleanup; } /* scratch now has the authorization data, so we decode it */ errcode = decode_krb5_authdata(&scratch, &(request->unenc_authdata)); free(scratch.data); if (errcode) { status = "AUTH_DECODE"; goto cleanup; } if ((errcode = concat_authorization_data(request->unenc_authdata, header_ticket->enc_part2->authorization_data, &enc_tkt_reply.authorization_data))) { status = "CONCAT_AUTH"; goto cleanup; } } else enc_tkt_reply.authorization_data = header_ticket->enc_part2->authorization_data; enc_tkt_reply.session = &session_key; enc_tkt_reply.client = header_ticket->enc_part2->client; enc_tkt_reply.transited.tr_type = KRB5_DOMAIN_X500_COMPRESS; enc_tkt_reply.transited.tr_contents = empty_string; /* equivalent of "" */ /* * Only add the realm of the presented tgt to the transited list if * it is different than the local realm (cross-realm) and it is different * than the realm of the client (since the realm of the client is already * implicitly part of the transited list and should not be explicitly * listed). */ /* realm compare is like strcmp, but knows how to deal with these args */ if (realm_compare(header_ticket->server, tgs_server) || realm_compare(header_ticket->server, enc_tkt_reply.client)) { /* tgt issued by local realm or issued by realm of client */ enc_tkt_reply.transited = header_ticket->enc_part2->transited; } else { /* tgt issued by some other realm and not the realm of the client */ /* assemble new transited field into allocated storage */ if (header_ticket->enc_part2->transited.tr_type != KRB5_DOMAIN_X500_COMPRESS) { status = "BAD_TRTYPE"; errcode = KRB5KDC_ERR_TRTYPE_NOSUPP; goto cleanup; } enc_tkt_transited.tr_type = KRB5_DOMAIN_X500_COMPRESS; enc_tkt_transited.magic = 0; enc_tkt_transited.tr_contents.magic = 0; enc_tkt_transited.tr_contents.data = 0; enc_tkt_transited.tr_contents.length = 0; enc_tkt_reply.transited = enc_tkt_transited; if ((errcode = add_to_transited(&header_ticket->enc_part2->transited.tr_contents, &enc_tkt_reply.transited.tr_contents, header_ticket->server, enc_tkt_reply.client, request->server))) { status = "ADD_TR_FAIL"; goto cleanup; } newtransited = 1; } if (!isflagset (request->kdc_options, KDC_OPT_DISABLE_TRANSITED_CHECK)) { unsigned int tlen; char *tdots; errcode = krb5_check_transited_list (kdc_context, &enc_tkt_reply.transited.tr_contents, krb5_princ_realm (kdc_context, header_ticket->enc_part2->client), krb5_princ_realm (kdc_context, request->server)); tlen = enc_tkt_reply.transited.tr_contents.length; tdots = tlen > 125 ? "..." : ""; tlen = tlen > 125 ? 125 : tlen; if (errcode == 0) { setflag (enc_tkt_reply.flags, TKT_FLG_TRANSIT_POLICY_CHECKED); } else if (errcode == KRB5KRB_AP_ERR_ILL_CR_TKT) krb5_klog_syslog (LOG_INFO, "bad realm transit path from '%s' to '%s' " "via '%.*s%s'", cname ? cname : "", sname ? sname : "", tlen, enc_tkt_reply.transited.tr_contents.data, tdots); else { const char *emsg = krb5_get_error_message(kdc_context, errcode); krb5_klog_syslog (LOG_ERR, "unexpected error checking transit from " "'%s' to '%s' via '%.*s%s': %s", cname ? cname : "", sname ? sname : "", tlen, enc_tkt_reply.transited.tr_contents.data, tdots, emsg); krb5_free_error_message(kdc_context, emsg); } } else krb5_klog_syslog (LOG_INFO, "not checking transit path"); if (reject_bad_transit && !isflagset (enc_tkt_reply.flags, TKT_FLG_TRANSIT_POLICY_CHECKED)) { errcode = KRB5KDC_ERR_POLICY; status = "BAD_TRANSIT"; goto cleanup; } ticket_reply.enc_part2 = &enc_tkt_reply; /* * If we are doing user-to-user authentication, then make sure * that the client for the second ticket matches the request * server, and then encrypt the ticket using the session key of * the second ticket. */ if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY)) { /* * Make sure the client for the second ticket matches * requested server. */ krb5_enc_tkt_part *t2enc = request->second_ticket[st_idx]->enc_part2; krb5_principal client2 = t2enc->client; if (!krb5_principal_compare(kdc_context, request->server, client2)) { if ((errcode = krb5_unparse_name(kdc_context, client2, &tmp))) tmp = 0; if (tmp != NULL) limit_string(tmp); audit_krb5kdc_tgs_req_2ndtktmm( (struct in_addr *)from->address->contents, (in_port_t)from->port, 0, cname, sname); krb5_klog_syslog(LOG_INFO, "TGS_REQ %s: 2ND_TKT_MISMATCH: " "authtime %d, %s for %s, 2nd tkt client %s", fromstring, authtime, cname ? cname : "", sname ? sname : "", tmp ? tmp : ""); errcode = KRB5KDC_ERR_SERVER_NOMATCH; goto cleanup; } ticket_reply.enc_part.kvno = 0; ticket_reply.enc_part.enctype = t2enc->session->enctype; if ((errcode = krb5_encrypt_tkt_part(kdc_context, t2enc->session, &ticket_reply))) { status = "2ND_TKT_ENCRYPT"; goto cleanup; } st_idx++; } else { /* * Find the server key */ if ((errcode = krb5_dbe_find_enctype(kdc_context, &server, -1, /* ignore keytype */ -1, /* Ignore salttype */ 0, /* Get highest kvno */ &server_key))) { status = "FINDING_SERVER_KEY"; goto cleanup; } /* convert server.key into a real key (it may be encrypted * in the database) */ if ((errcode = krb5_dbekd_decrypt_key_data(kdc_context, &master_keyblock, server_key, &encrypting_key, NULL))) { status = "DECRYPT_SERVER_KEY"; goto cleanup; } errcode = krb5_encrypt_tkt_part(kdc_context, &encrypting_key, &ticket_reply); krb5_free_keyblock_contents(kdc_context, &encrypting_key); if (errcode) { status = "TKT_ENCRYPT"; goto cleanup; } ticket_reply.enc_part.kvno = server_key->key_data_kvno; } /* Start assembling the response */ reply.msg_type = KRB5_TGS_REP; reply.padata = 0; /* always */ reply.client = header_ticket->enc_part2->client; reply.enc_part.kvno = 0; /* We are using the session key */ reply.ticket = &ticket_reply; reply_encpart.session = &session_key; reply_encpart.nonce = request->nonce; /* copy the time fields EXCEPT for authtime; its location is used for ktime */ reply_encpart.times = enc_tkt_reply.times; reply_encpart.times.authtime = header_ticket->enc_part2->times.authtime; /* starttime is optional, and treated as authtime if not present. so we can nuke it if it matches */ if (enc_tkt_reply.times.starttime == enc_tkt_reply.times.authtime) enc_tkt_reply.times.starttime = 0; nolrentry.lr_type = KRB5_LRQ_NONE; nolrentry.value = 0; nolrarray[0] = &nolrentry; nolrarray[1] = 0; reply_encpart.last_req = nolrarray; /* not available for TGS reqs */ reply_encpart.key_exp = 0; /* ditto */ reply_encpart.flags = enc_tkt_reply.flags; reply_encpart.server = ticket_reply.server; /* use the session key in the ticket, unless there's a subsession key in the AP_REQ */ reply.enc_part.enctype = subkey ? subkey->enctype : header_ticket->enc_part2->session->enctype; errcode = krb5_encode_kdc_rep(kdc_context, KRB5_TGS_REP, &reply_encpart, subkey ? 1 : 0, subkey ? subkey : header_ticket->enc_part2->session, &reply, response); if (errcode) { status = "ENCODE_KDC_REP"; } else { status = "ISSUE"; } if (ticket_reply.enc_part.ciphertext.data) { memset(ticket_reply.enc_part.ciphertext.data, 0, ticket_reply.enc_part.ciphertext.length); free(ticket_reply.enc_part.ciphertext.data); ticket_reply.enc_part.ciphertext.data = NULL; } /* these parts are left on as a courtesy from krb5_encode_kdc_rep so we can use them in raw form if needed. But, we don't... */ if (reply.enc_part.ciphertext.data) { memset(reply.enc_part.ciphertext.data, 0, reply.enc_part.ciphertext.length); free(reply.enc_part.ciphertext.data); reply.enc_part.ciphertext.data = NULL; } cleanup: if (status) { const char * emsg = NULL; audit_krb5kdc_tgs_req((struct in_addr *)from->address->contents, (in_port_t)from->port, 0, cname ? cname : "", sname ? sname : "", errcode); if (!errcode) rep_etypes2str(rep_etypestr, sizeof(rep_etypestr), &reply); if (errcode) emsg = krb5_get_error_message (kdc_context, errcode); krb5_klog_syslog(LOG_INFO, "TGS_REQ (%s) %s: %s: authtime %d, " "%s%s %s for %s%s%s", ktypestr, fromstring, status, authtime, !errcode ? rep_etypestr : "", !errcode ? "," : "", cname ? cname : "", sname ? sname : "", errcode ? ", " : "", errcode ? emsg : ""); if (errcode) krb5_free_error_message (kdc_context, emsg); } if (errcode) { int got_err = 0; if (status == 0) { status = krb5_get_error_message (kdc_context, errcode); got_err = 1; } errcode -= ERROR_TABLE_BASE_krb5; if (errcode < 0 || errcode > 128) errcode = KRB_ERR_GENERIC; retval = prepare_error_tgs(request, header_ticket, errcode, fromstring, response, status); if (got_err) { krb5_free_error_message (kdc_context, status); status = 0; } } if (header_ticket) krb5_free_ticket(kdc_context, header_ticket); if (request) krb5_free_kdc_req(kdc_context, request); if (cname) free(cname); if (sname) free(sname); if (nprincs) krb5_db_free_principal(kdc_context, &server, 1); if (session_key.contents) krb5_free_keyblock_contents(kdc_context, &session_key); if (newtransited) free(enc_tkt_reply.transited.tr_contents.data); return retval; } static krb5_error_code prepare_error_tgs (krb5_kdc_req *request, krb5_ticket *ticket, int error, const char *ident, krb5_data **response, const char *status) { krb5_error errpkt; krb5_error_code retval; krb5_data *scratch; errpkt.ctime = request->nonce; errpkt.cusec = 0; if ((retval = krb5_us_timeofday(kdc_context, &errpkt.stime, &errpkt.susec))) return(retval); errpkt.error = error; errpkt.server = request->server; if (ticket && ticket->enc_part2) errpkt.client = ticket->enc_part2->client; else errpkt.client = 0; errpkt.text.length = strlen(status) + 1; if (!(errpkt.text.data = malloc(errpkt.text.length))) return ENOMEM; (void) strcpy(errpkt.text.data, status); if (!(scratch = (krb5_data *)malloc(sizeof(*scratch)))) { free(errpkt.text.data); return ENOMEM; } errpkt.e_data.length = 0; errpkt.e_data.data = 0; retval = krb5_mk_error(kdc_context, &errpkt, scratch); free(errpkt.text.data); if (retval) free(scratch); else *response = scratch; return retval; } /* * The request seems to be for a ticket-granting service somewhere else, * but we don't have a ticket for the final TGS. Try to give the requestor * some intermediate realm. */ static void find_alternate_tgs(krb5_kdc_req *request, krb5_db_entry *server, krb5_boolean *more, int *nprincs, const krb5_fulladdr *from, char *cname) { krb5_error_code retval; krb5_principal *plist, *pl2; krb5_data tmp; *nprincs = 0; *more = FALSE; /* * Call to krb5_princ_component is normally not safe but is so * here only because find_alternate_tgs() is only called from * somewhere that has already checked the number of components in * the principal. */ if ((retval = krb5_walk_realm_tree(kdc_context, krb5_princ_realm(kdc_context, request->server), krb5_princ_component(kdc_context, request->server, 1), &plist, KRB5_REALM_BRANCH_CHAR))) return; /* move to the end */ for (pl2 = plist; *pl2; pl2++); /* the first entry in this array is for krbtgt/local@local, so we ignore it */ while (--pl2 > plist) { *nprincs = 1; tmp = *krb5_princ_realm(kdc_context, *pl2); krb5_princ_set_realm(kdc_context, *pl2, krb5_princ_realm(kdc_context, tgs_server)); retval = krb5_db_get_principal(kdc_context, *pl2, server, nprincs, more); krb5_princ_set_realm(kdc_context, *pl2, &tmp); if (retval) { *nprincs = 0; *more = FALSE; krb5_free_realm_tree(kdc_context, plist); return; } if (*more) { krb5_db_free_principal(kdc_context, server, *nprincs); continue; } else if (*nprincs == 1) { /* Found it! */ krb5_principal tmpprinc; char *sname; tmp = *krb5_princ_realm(kdc_context, *pl2); krb5_princ_set_realm(kdc_context, *pl2, krb5_princ_realm(kdc_context, tgs_server)); if ((retval = krb5_copy_principal(kdc_context, *pl2, &tmpprinc))) { krb5_db_free_principal(kdc_context, server, *nprincs); krb5_princ_set_realm(kdc_context, *pl2, &tmp); continue; } krb5_princ_set_realm(kdc_context, *pl2, &tmp); krb5_free_principal(kdc_context, request->server); request->server = tmpprinc; if (krb5_unparse_name(kdc_context, request->server, &sname)) { audit_krb5kdc_tgs_req_alt_tgt( (struct in_addr *)from->address->contents, (in_port_t)from->port, 0, cname, "", 0); krb5_klog_syslog(LOG_INFO, "TGS_REQ: issuing alternate TGT"); } else { limit_string(sname); audit_krb5kdc_tgs_req_alt_tgt( (struct in_addr *)from->address->contents, (in_port_t)from->port, 0, cname, sname, 0); krb5_klog_syslog(LOG_INFO, "TGS_REQ: issuing TGT %s", sname); free(sname); } return; } krb5_db_free_principal(kdc_context, server, *nprincs); continue; } *nprincs = 0; *more = FALSE; krb5_free_realm_tree(kdc_context, plist); return; } /* * kdc/extern.c * * Copyright 1990 by the Massachusetts Institute of Technology. * * 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. * * * allocations of extern stuff */ #include "k5-int.h" #include "extern.h" /* real declarations of KDC's externs */ kdc_realm_t **kdc_realmlist = (kdc_realm_t **) NULL; int kdc_numrealms = 0; kdc_realm_t *kdc_active_realm = (kdc_realm_t *) NULL; krb5_data empty_string = {0, 0, ""}; krb5_timestamp kdc_infinity = KRB5_KDB_EXPIRATION; krb5_rcache kdc_rcache = (krb5_rcache) NULL; krb5_keyblock psr_key; volatile int signal_requests_exit = 0; /* gets set when signal hits */ volatile int signal_requests_hup = 0; /* ditto */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef __KRB5_KDC_EXTERN__ #define __KRB5_KDC_EXTERN__ #include #ifdef __cplusplus extern "C" { #endif /* * Copyright 1990,2001 by the Massachusetts Institute of Technology. * * 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. * * * <<< Description >>> */ typedef struct __kdc_realm_data { /* * General Kerberos per-realm data. */ char * realm_name; /* Realm name */ /* XXX the real context should go away once the db_context is done. * The db_context is then associated with the realm keytab using * krb5_ktkdb_resolv(). There should be nothing in the context which * cannot span multiple realms -- proven */ krb5_context realm_context; /* Context to be used for realm */ krb5_keytab realm_keytab; /* keytab to be used for this realm */ char * realm_profile; /* Profile file for this realm */ /* * Database per-realm data. */ char * realm_dbname; /* Database name for realm */ char * realm_stash; /* Stash file name for realm */ char * realm_mpname; /* Master principal name for realm */ krb5_principal realm_mprinc; /* Master principal for realm */ krb5_keyblock realm_mkey; /* Master key for this realm */ /* * TGS per-realm data. */ krb5_principal realm_tgsprinc; /* TGS principal for this realm */ /* * Other per-realm data. */ char *realm_ports; /* Per-realm KDC UDP port */ char *realm_tcp_ports; /* Per-realm KDC TCP port */ /* * Per-realm parameters. */ krb5_deltat realm_maxlife; /* Maximum ticket life for realm */ krb5_deltat realm_maxrlife; /* Maximum renewable life for realm */ krb5_boolean realm_reject_bad_transit; /* Accept unverifiable transited_realm ? */ } kdc_realm_t; extern kdc_realm_t **kdc_realmlist; extern int kdc_numrealms; extern kdc_realm_t *kdc_active_realm; kdc_realm_t *find_realm_data (char *, krb5_ui_4); /* * Replace previously used global variables with the active (e.g. request's) * realm data. This allows us to support multiple realms with minimal logic * changes. */ #define kdc_context kdc_active_realm->realm_context #define max_life_for_realm kdc_active_realm->realm_maxlife #define max_renewable_life_for_realm kdc_active_realm->realm_maxrlife #define master_keyblock kdc_active_realm->realm_mkey #define master_princ kdc_active_realm->realm_mprinc #define tgs_server_struct *(kdc_active_realm->realm_tgsprinc) #define tgs_server kdc_active_realm->realm_tgsprinc #define dbm_db_name kdc_active_realm->realm_dbname #define primary_port kdc_active_realm->realm_pport #define reject_bad_transit kdc_active_realm->realm_reject_bad_transit /* various externs for KDC */ extern krb5_data empty_string; /* an empty string */ extern krb5_timestamp kdc_infinity; /* greater than all other timestamps */ extern krb5_rcache kdc_rcache; /* replay cache */ extern krb5_keyblock psr_key; /* key for predicted sam response */ extern volatile int signal_requests_exit; extern volatile int signal_requests_hup; /* libbsm */ extern void audit_krb5kdc_as_req(struct in_addr *, in_port_t, in_port_t, char *, char *, int); extern void audit_krb5kdc_tgs_req(struct in_addr *, in_port_t, in_port_t, char *, char *, int); extern void audit_krb5kdc_tgs_req_2ndtktmm(struct in_addr *, in_port_t, in_port_t, char *, char *); extern void audit_krb5kdc_tgs_req_alt_tgt(struct in_addr *, in_port_t, in_port_t, char *, char *, int); #ifdef __cplusplus } #endif #endif /* !__KRB5_KDC_EXTERN__ */ /* * Copyright (c) 1997-2000 by Sun Microsystems, Inc. * All rights reserved. */ #ifndef _KDC5_ERR_H #define _KDC5_ERR_H #ifdef __cplusplus extern "C" { #endif /* * kdc5_err.h: * This file is automatically generated; please do not edit it. */ #define KDC5_RCSID (-1779992064L) #define KDC5_NOPORT (-1779992063L) #define KDC5_NONET (-1779992062L) #define KDC5_IO_RESPONSE (-1779992061L) #define ERROR_TABLE_BASE_kdc5 (-1779992064L) /* for compatibility with older versions... */ #define kdc5_err_base ERROR_TABLE_BASE_kdc5 #ifdef __cplusplus } #endif #endif /* !_KDC5_ERR_H */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * kdc/kdc_preauth.c * * Copyright 1995, 2003 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. * * Preauthentication routines for the KDC. */ /* * Copyright (C) 1998 by the FundsXpress, INC. * * 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 FundsXpress. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. FundsXpress makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #include "k5-int.h" #include "kdc_util.h" #include "extern.h" #include "com_err.h" #include #include #include "adm_proto.h" #include #include #include #include "preauth_plugin.h" #if TARGET_OS_MAC static const char *objdirs[] = { KRB5_PLUGIN_BUNDLE_DIR, LIBDIR "/krb5/plugins/preauth", NULL }; /* should be a list */ #else static const char *objdirs[] = { LIBDIR "/krb5/plugins/preauth", NULL }; #endif /* XXX This is ugly and should be in a header file somewhere */ #ifndef KRB5INT_DES_TYPES_DEFINED #define KRB5INT_DES_TYPES_DEFINED typedef unsigned char des_cblock[8]; /* crypto-block size */ #endif typedef des_cblock mit_des_cblock; extern void mit_des_fixup_key_parity (mit_des_cblock ); extern int mit_des_is_weak_key (mit_des_cblock ); typedef struct _krb5_preauth_systems { const char *name; int type; int flags; void *plugin_context; preauth_server_init_proc init; preauth_server_fini_proc fini; preauth_server_edata_proc get_edata; preauth_server_verify_proc verify_padata; preauth_server_return_proc return_padata; preauth_server_free_reqcontext_proc free_pa_reqctx; } krb5_preauth_systems; static krb5_error_code verify_enc_timestamp (krb5_context, krb5_db_entry *client, krb5_data *req_pkt, krb5_kdc_req *request, krb5_enc_tkt_part * enc_tkt_reply, krb5_pa_data *data, preauth_get_entry_data_proc get_entry_data, void *pa_system_context, void **pa_request_context, krb5_data **e_data, krb5_authdata ***authz_data); static krb5_error_code get_etype_info (krb5_context, krb5_kdc_req *request, krb5_db_entry *client, krb5_db_entry *server, preauth_get_entry_data_proc get_entry_data, void *pa_system_context, krb5_pa_data *data); static krb5_error_code get_etype_info2(krb5_context context, krb5_kdc_req *request, krb5_db_entry *client, krb5_db_entry *server, preauth_get_entry_data_proc get_entry_data, void *pa_system_context, krb5_pa_data *pa_data); static krb5_error_code etype_info_as_rep_helper(krb5_context context, krb5_pa_data * padata, krb5_db_entry *client, krb5_kdc_req *request, krb5_kdc_rep *reply, krb5_key_data *client_key, krb5_keyblock *encrypting_key, krb5_pa_data **send_pa, int etype_info2); static krb5_error_code return_etype_info(krb5_context, krb5_pa_data * padata, krb5_db_entry *client, krb5_data *req_pkt, krb5_kdc_req *request, krb5_kdc_rep *reply, krb5_key_data *client_key, krb5_keyblock *encrypting_key, krb5_pa_data **send_pa, preauth_get_entry_data_proc get_entry_data, void *pa_system_context, void **pa_request_context); static krb5_error_code return_etype_info2(krb5_context, krb5_pa_data * padata, krb5_db_entry *client, krb5_data *req_pkt, krb5_kdc_req *request, krb5_kdc_rep *reply, krb5_key_data *client_key, krb5_keyblock *encrypting_key, krb5_pa_data **send_pa, preauth_get_entry_data_proc get_entry_data, void *pa_system_context, void **pa_request_context); static krb5_error_code return_pw_salt (krb5_context, krb5_pa_data * padata, krb5_db_entry *client, krb5_data *req_pkt, krb5_kdc_req *request, krb5_kdc_rep *reply, krb5_key_data *client_key, krb5_keyblock *encrypting_key, krb5_pa_data **send_pa, preauth_get_entry_data_proc get_entry_data, void *pa_system_context, void **pa_request_context); /* SAM preauth support */ static krb5_error_code verify_sam_response (krb5_context, krb5_db_entry *client, krb5_data *req_pkt, krb5_kdc_req *request, krb5_enc_tkt_part * enc_tkt_reply, krb5_pa_data *data, preauth_get_entry_data_proc get_entry_data, void *pa_module_context, void **pa_request_context, krb5_data **e_data, krb5_authdata ***authz_data); static krb5_error_code get_sam_edata (krb5_context, krb5_kdc_req *request, krb5_db_entry *client, krb5_db_entry *server, preauth_get_entry_data_proc get_entry_data, void *pa_module_context, krb5_pa_data *data); static krb5_error_code return_sam_data (krb5_context, krb5_pa_data * padata, krb5_db_entry *client, krb5_data *req_pkt, krb5_kdc_req *request, krb5_kdc_rep *reply, krb5_key_data *client_key, krb5_keyblock *encrypting_key, krb5_pa_data **send_pa, preauth_get_entry_data_proc get_entry_data, void *pa_module_context, void **pa_request_context); static krb5_preauth_systems static_preauth_systems[] = { { "timestamp", KRB5_PADATA_ENC_TIMESTAMP, 0, NULL, NULL, NULL, 0, verify_enc_timestamp, 0 }, { "etype-info", KRB5_PADATA_ETYPE_INFO, 0, NULL, NULL, NULL, get_etype_info, 0, return_etype_info }, { "etype-info2", KRB5_PADATA_ETYPE_INFO2, 0, NULL, NULL, NULL, get_etype_info2, 0, return_etype_info2 }, { "pw-salt", KRB5_PADATA_PW_SALT, PA_PSEUDO, /* Don't include this in the error list */ NULL, NULL, NULL, 0, 0, return_pw_salt }, { "sam-response", KRB5_PADATA_SAM_RESPONSE, 0, NULL, NULL, NULL, 0, verify_sam_response, return_sam_data }, { "sam-challenge", KRB5_PADATA_SAM_CHALLENGE, PA_HARDWARE, /* causes get_preauth_hint_list to use this */ NULL, NULL, NULL, get_sam_edata, 0, 0 }, { "[end]", -1,} }; static krb5_preauth_systems *preauth_systems; static int n_preauth_systems; static struct plugin_dir_handle preauth_plugins; krb5_error_code load_preauth_plugins(krb5_context context) { struct errinfo err; void **preauth_plugins_ftables; struct krb5plugin_preauth_server_ftable_v1 *ftable; int module_count, i, j, k; void *plugin_context; preauth_server_init_proc server_init_proc = NULL; char **kdc_realm_names = NULL; memset(&err, 0, sizeof(err)); /* Attempt to load all of the preauth plugins we can find. */ PLUGIN_DIR_INIT(&preauth_plugins); if (PLUGIN_DIR_OPEN(&preauth_plugins) == 0) { if (krb5int_open_plugin_dirs(objdirs, NULL, &preauth_plugins, &err) != 0) { return KRB5_PLUGIN_NO_HANDLE; } } /* Get the method tables provided by the loaded plugins. */ preauth_plugins_ftables = NULL; if (krb5int_get_plugin_dir_data(&preauth_plugins, "preauthentication_server_1", &preauth_plugins_ftables, &err) != 0) { return KRB5_PLUGIN_NO_HANDLE; } /* Count the valid modules. */ module_count = sizeof(static_preauth_systems) / sizeof(static_preauth_systems[0]); if (preauth_plugins_ftables != NULL) { for (i = 0; preauth_plugins_ftables[i] != NULL; i++) { ftable = preauth_plugins_ftables[i]; if ((ftable->flags_proc == NULL) && (ftable->edata_proc == NULL) && (ftable->verify_proc == NULL) && (ftable->return_proc == NULL)) { continue; } for (j = 0; ftable->pa_type_list != NULL && ftable->pa_type_list[j] > 0; j++) { module_count++; } } } /* Build the complete list of supported preauthentication options, and * leave room for a terminator entry. */ preauth_systems = malloc(sizeof(krb5_preauth_systems) * (module_count + 1)); if (preauth_systems == NULL) { krb5int_free_plugin_dir_data(preauth_plugins_ftables); return ENOMEM; } /* Build a list of the names of the supported realms for this KDC. * The list of names is terminated with a NULL. */ kdc_realm_names = malloc(sizeof(char *) * (kdc_numrealms + 1)); if (kdc_realm_names == NULL) { krb5int_free_plugin_dir_data(preauth_plugins_ftables); return ENOMEM; } for (i = 0; i < kdc_numrealms; i++) { kdc_realm_names[i] = kdc_realmlist[i]->realm_name; } kdc_realm_names[i] = NULL; /* Add the locally-supplied mechanisms to the dynamic list first. */ for (i = 0, k = 0; i < sizeof(static_preauth_systems) / sizeof(static_preauth_systems[0]); i++) { if (static_preauth_systems[i].type == -1) break; preauth_systems[k] = static_preauth_systems[i]; /* Try to initialize the preauth system. If it fails, we'll remove it * from the list of systems we'll be using. */ plugin_context = NULL; server_init_proc = static_preauth_systems[i].init; if ((server_init_proc != NULL) && ((*server_init_proc)(context, &plugin_context, (const char **)kdc_realm_names) != 0)) { memset(&preauth_systems[k], 0, sizeof(preauth_systems[k])); continue; } preauth_systems[k].plugin_context = plugin_context; k++; } /* Now add the dynamically-loaded mechanisms to the list. */ if (preauth_plugins_ftables != NULL) { for (i = 0; preauth_plugins_ftables[i] != NULL; i++) { ftable = preauth_plugins_ftables[i]; if ((ftable->flags_proc == NULL) && (ftable->edata_proc == NULL) && (ftable->verify_proc == NULL) && (ftable->return_proc == NULL)) { continue; } plugin_context = NULL; for (j = 0; ftable->pa_type_list != NULL && ftable->pa_type_list[j] > 0; j++) { /* Try to initialize the plugin. If it fails, we'll remove it * from the list of modules we'll be using. */ if (j == 0) { server_init_proc = ftable->init_proc; if (server_init_proc != NULL) { krb5_error_code initerr; initerr = (*server_init_proc)(context, &plugin_context, (const char **)kdc_realm_names); if (initerr) { const char *emsg; emsg = krb5_get_error_message(context, initerr); if (emsg) { krb5_klog_syslog(LOG_ERR, "preauth %s failed to initialize: %s", ftable->name, emsg); krb5_free_error_message(context, emsg); } memset(&preauth_systems[k], 0, sizeof(preauth_systems[k])); break; /* skip all modules in this plugin */ } } } preauth_systems[k].name = ftable->name; preauth_systems[k].type = ftable->pa_type_list[j]; if (ftable->flags_proc != NULL) preauth_systems[k].flags = ftable->flags_proc(context, preauth_systems[k].type); else preauth_systems[k].flags = 0; preauth_systems[k].plugin_context = plugin_context; preauth_systems[k].init = server_init_proc; /* Only call fini once for each plugin */ if (j == 0) preauth_systems[k].fini = ftable->fini_proc; else preauth_systems[k].fini = NULL; preauth_systems[k].get_edata = ftable->edata_proc; preauth_systems[k].verify_padata = ftable->verify_proc; preauth_systems[k].return_padata = ftable->return_proc; preauth_systems[k].free_pa_reqctx = ftable->freepa_reqcontext_proc; k++; } } krb5int_free_plugin_dir_data(preauth_plugins_ftables); } free(kdc_realm_names); n_preauth_systems = k; /* Add the end-of-list marker. */ preauth_systems[k].name = "[end]"; preauth_systems[k].type = -1; return 0; } krb5_error_code unload_preauth_plugins(krb5_context context) { int i; if (preauth_systems != NULL) { for (i = 0; i < n_preauth_systems; i++) { if (preauth_systems[i].fini != NULL) { (*preauth_systems[i].fini)(context, preauth_systems[i].plugin_context); } memset(&preauth_systems[i], 0, sizeof(preauth_systems[i])); } free(preauth_systems); preauth_systems = NULL; n_preauth_systems = 0; krb5int_close_plugin_dirs(&preauth_plugins); } return 0; } /* * The make_padata_context() function creates a space for storing any context * information which will be needed by return_padata() later. Each preauth * type gets a context storage location of its own. */ struct request_pa_context { int n_contexts; struct { krb5_preauth_systems *pa_system; void *pa_context; } *contexts; }; static krb5_error_code make_padata_context(krb5_context context, void **padata_context) { int i; struct request_pa_context *ret; ret = malloc(sizeof(*ret)); if (ret == NULL) { return ENOMEM; } ret->n_contexts = n_preauth_systems; ret->contexts = malloc(sizeof(ret->contexts[0]) * ret->n_contexts); if (ret->contexts == NULL) { free(ret); return ENOMEM; } memset(ret->contexts, 0, sizeof(ret->contexts[0]) * ret->n_contexts); for (i = 0; i < ret->n_contexts; i++) { ret->contexts[i].pa_system = &preauth_systems[i]; ret->contexts[i].pa_context = NULL; } *padata_context = ret; return 0; } /* * The free_padata_context function frees any context information pointers * which the check_padata() function created but which weren't already cleaned * up by return_padata(). */ krb5_error_code free_padata_context(krb5_context kcontext, void **padata_context) { struct request_pa_context *context; krb5_preauth_systems *preauth_system; void **pctx, *mctx; int i; if (padata_context == NULL) return 0; context = *padata_context; for (i = 0; i < context->n_contexts; i++) { if (context->contexts[i].pa_context != NULL) { preauth_system = context->contexts[i].pa_system; mctx = preauth_system->plugin_context; if (preauth_system->free_pa_reqctx != NULL) { pctx = &context->contexts[i].pa_context; (*preauth_system->free_pa_reqctx)(kcontext, mctx, pctx); } context->contexts[i].pa_context = NULL; } } free(context->contexts); free(context); return 0; } /* Retrieve a specified tl_data item from the given entry, and return its * contents in a new krb5_data, which must be freed by the caller. */ static krb5_error_code get_entry_tl_data(krb5_context context, krb5_db_entry *entry, krb5_int16 tl_data_type, krb5_data **result) { krb5_tl_data *tl; for (tl = entry->tl_data; tl != NULL; tl = tl->tl_data_next) { if (tl->tl_data_type == tl_data_type) { *result = malloc(sizeof(krb5_data)); if (*result == NULL) { return ENOMEM; } (*result)->magic = KV5M_DATA; (*result)->data = malloc(tl->tl_data_length); if ((*result)->data == NULL) { free(*result); *result = NULL; return ENOMEM; } memcpy((*result)->data, tl->tl_data_contents, tl->tl_data_length); return 0; } } return ENOENT; } /* * Retrieve a specific piece of information pertaining to the entry or the * request and return it in a new krb5_data item which the caller must free. * * This may require massaging data into a contrived format, but it will * hopefully keep us from having to reveal library-internal functions to * modules. */ static krb5_error_code get_entry_data(krb5_context context, krb5_kdc_req *request, krb5_db_entry *entry, krb5_int32 type, krb5_data **result) { int i, k; krb5_data *ret; krb5_deltat *delta; krb5_keyblock *keys; krb5_key_data *entry_key; switch (type) { case krb5plugin_preauth_entry_request_certificate: return get_entry_tl_data(context, entry, KRB5_TL_USER_CERTIFICATE, result); break; case krb5plugin_preauth_entry_max_time_skew: ret = malloc(sizeof(krb5_data)); if (ret == NULL) return ENOMEM; delta = malloc(sizeof(krb5_deltat)); if (delta == NULL) { free(ret); return ENOMEM; } *delta = context->clockskew; ret->data = (char *) delta; ret->length = sizeof(*delta); *result = ret; return 0; break; case krb5plugin_preauth_keys: ret = malloc(sizeof(krb5_data)); if (ret == NULL) return ENOMEM; keys = malloc(sizeof(krb5_keyblock) * (request->nktypes + 1)); if (keys == NULL) { free(ret); return ENOMEM; } ret->data = (char *) keys; ret->length = sizeof(krb5_keyblock) * (request->nktypes + 1); memset(ret->data, 0, ret->length); k = 0; for (i = 0; i < request->nktypes; i++) { entry_key = NULL; if (krb5_dbe_find_enctype(context, entry, request->ktype[i], -1, 0, &entry_key) != 0) continue; if (krb5_dbekd_decrypt_key_data(context, &master_keyblock, entry_key, &keys[k], NULL) != 0) { if (keys[k].contents != NULL) krb5_free_keyblock_contents(context, &keys[k]); memset(&keys[k], 0, sizeof(keys[k])); continue; } k++; } if (k > 0) { *result = ret; return 0; } else { free(keys); free(ret); } break; case krb5plugin_preauth_request_body: ret = NULL; encode_krb5_kdc_req_body(request, &ret); if (ret != NULL) { *result = ret; return 0; } return ASN1_PARSE_ERROR; break; default: break; } return ENOENT; } static krb5_error_code find_pa_system(int type, krb5_preauth_systems **preauth) { krb5_preauth_systems *ap; ap = preauth_systems ? preauth_systems : static_preauth_systems; while ((ap->type != -1) && (ap->type != type)) ap++; if (ap->type == -1) return(KRB5_PREAUTH_BAD_TYPE); *preauth = ap; return 0; } static krb5_error_code find_pa_context(krb5_preauth_systems *pa_sys, struct request_pa_context *context, void ***pa_context) { int i; *pa_context = 0; if (context == NULL) return KRB5KRB_ERR_GENERIC; for (i = 0; i < context->n_contexts; i++) { if (context->contexts[i].pa_system == pa_sys) { *pa_context = &context->contexts[i].pa_context; return 0; } } return KRB5KRB_ERR_GENERIC; } /* * Create a list of indices into the preauth_systems array, sorted by order of * preference. */ static krb5_boolean pa_list_includes(krb5_pa_data **pa_data, krb5_preauthtype pa_type) { while (*pa_data != NULL) { if ((*pa_data)->pa_type == pa_type) return TRUE; pa_data++; } return FALSE; } static void sort_pa_order(krb5_context context, krb5_kdc_req *request, int *pa_order) { int i, j, k, n_repliers, n_key_replacers; /* First, set up the default order. */ i = 0; for (j = 0; j < n_preauth_systems; j++) { if (preauth_systems[j].return_padata != NULL) pa_order[i++] = j; } n_repliers = i; pa_order[n_repliers] = -1; /* Reorder so that PA_REPLACES_KEY modules are listed first. */ for (i = 0; i < n_repliers; i++) { /* If this module replaces the key, then it's okay to leave it where it * is in the order. */ if (preauth_systems[pa_order[i]].flags & PA_REPLACES_KEY) continue; /* If not, search for a module which does, and swap in the first one we * find. */ for (j = i + 1; j < n_repliers; j++) { if (preauth_systems[pa_order[j]].flags & PA_REPLACES_KEY) { k = pa_order[j]; pa_order[j] = pa_order[i]; pa_order[i] = k; break; } } } if (request->padata != NULL) { /* Now reorder the subset of modules which replace the key, * bubbling those which handle pa_data types provided by the * client ahead of the others. */ for (i = 0; preauth_systems[pa_order[i]].flags & PA_REPLACES_KEY; i++) { continue; } n_key_replacers = i; for (i = 0; i < n_key_replacers; i++) { if (pa_list_includes(request->padata, preauth_systems[pa_order[i]].type)) continue; for (j = i + 1; j < n_key_replacers; j++) { if (pa_list_includes(request->padata, preauth_systems[pa_order[j]].type)) { k = pa_order[j]; pa_order[j] = pa_order[i]; pa_order[i] = k; break; } } } } #ifdef DEBUG krb5_klog_syslog(LOG_DEBUG, "original preauth mechanism list:"); for (i = 0; i < n_preauth_systems; i++) { if (preauth_systems[i].return_padata != NULL) krb5_klog_syslog(LOG_DEBUG, "... %s(%d)", preauth_systems[i].name, preauth_systems[i].type); } krb5_klog_syslog(LOG_DEBUG, "sorted preauth mechanism list:"); for (i = 0; pa_order[i] != -1; i++) { krb5_klog_syslog(LOG_DEBUG, "... %s(%d)", preauth_systems[pa_order[i]].name, preauth_systems[pa_order[i]].type); } #endif } const char *missing_required_preauth(krb5_db_entry *client, krb5_db_entry *server, krb5_enc_tkt_part *enc_tkt_reply) { #if 0 /* * If this is the pwchange service, and the pre-auth bit is set, * allow it even if the HW preauth would normally be required. * * Sandia national labs wanted this for some strange reason... we * leave it disabled normally. */ if (isflagset(server->attributes, KRB5_KDB_PWCHANGE_SERVICE) && isflagset(enc_tkt_reply->flags, TKT_FLG_PRE_AUTH)) return 0; #endif #ifdef DEBUG krb5_klog_syslog (LOG_DEBUG, "client needs %spreauth, %shw preauth; request has %spreauth, %shw preauth", isflagset (client->attributes, KRB5_KDB_REQUIRES_PRE_AUTH) ? "" : "no ", isflagset (client->attributes, KRB5_KDB_REQUIRES_HW_AUTH) ? "" : "no ", isflagset (enc_tkt_reply->flags, TKT_FLG_PRE_AUTH) ? "" : "no ", isflagset (enc_tkt_reply->flags, TKT_FLG_HW_AUTH) ? "" : "no "); #endif if (isflagset(client->attributes, KRB5_KDB_REQUIRES_PRE_AUTH) && !isflagset(enc_tkt_reply->flags, TKT_FLG_PRE_AUTH)) return "NEEDED_PREAUTH"; if (isflagset(client->attributes, KRB5_KDB_REQUIRES_HW_AUTH) && !isflagset(enc_tkt_reply->flags, TKT_FLG_HW_AUTH)) return "NEEDED_HW_PREAUTH"; return 0; } void get_preauth_hint_list(krb5_kdc_req *request, krb5_db_entry *client, krb5_db_entry *server, krb5_data *e_data) { int hw_only; krb5_preauth_systems *ap; krb5_pa_data **pa_data, **pa; krb5_data *edat; krb5_error_code retval; /* Zero these out in case we need to abort */ e_data->length = 0; e_data->data = 0; hw_only = isflagset(client->attributes, KRB5_KDB_REQUIRES_HW_AUTH); pa_data = malloc(sizeof(krb5_pa_data *) * (n_preauth_systems+1)); if (pa_data == 0) return; memset(pa_data, 0, sizeof(krb5_pa_data *) * (n_preauth_systems+1)); pa = pa_data; for (ap = preauth_systems; ap->type != -1; ap++) { if (hw_only && !(ap->flags & PA_HARDWARE)) continue; if (ap->flags & PA_PSEUDO) continue; *pa = malloc(sizeof(krb5_pa_data)); if (*pa == 0) goto errout; memset(*pa, 0, sizeof(krb5_pa_data)); (*pa)->magic = KV5M_PA_DATA; (*pa)->pa_type = ap->type; if (ap->get_edata) { retval = (ap->get_edata)(kdc_context, request, client, server, get_entry_data, ap->plugin_context, *pa); if (retval) { /* just failed on this type, continue */ free(*pa); *pa = 0; continue; } } pa++; } if (pa_data[0] == 0) { krb5_klog_syslog (LOG_INFO, "%spreauth required but hint list is empty", hw_only ? "hw" : ""); } retval = encode_krb5_padata_sequence((krb5_pa_data * const *) pa_data, &edat); if (retval) goto errout; *e_data = *edat; free(edat); errout: krb5_free_pa_data(kdc_context, pa_data); return; } /* * Add authorization data returned from preauth modules to the ticket * It is assumed that ad is a "null-terminated" array of krb5_authdata ptrs */ static krb5_error_code add_authorization_data(krb5_enc_tkt_part *enc_tkt_part, krb5_authdata **ad) { krb5_authdata **newad; int oldones, newones; int i; if (enc_tkt_part == NULL || ad == NULL) return EINVAL; for (newones = 0; ad[newones] != NULL; newones++); if (newones == 0) return 0; /* nothing to add */ if (enc_tkt_part->authorization_data == NULL) oldones = 0; else for (oldones = 0; enc_tkt_part->authorization_data[oldones] != NULL; oldones++); newad = malloc((oldones + newones + 1) * sizeof(krb5_authdata *)); if (newad == NULL) return ENOMEM; /* Copy any existing pointers */ for (i = 0; i < oldones; i++) newad[i] = enc_tkt_part->authorization_data[i]; /* Add the new ones */ for (i = 0; i < newones; i++) newad[oldones+i] = ad[i]; /* Terminate the new list */ newad[oldones+i] = NULL; /* Free any existing list */ if (enc_tkt_part->authorization_data != NULL) free(enc_tkt_part->authorization_data); /* Install our new list */ enc_tkt_part->authorization_data = newad; return 0; } /* * This routine is called to verify the preauthentication information * for a V5 request. * * Returns 0 if the pre-authentication is valid, non-zero to indicate * an error code of some sort. */ krb5_error_code check_padata (krb5_context context, krb5_db_entry *client, krb5_data *req_pkt, krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply, void **padata_context, krb5_data *e_data) { krb5_error_code retval = 0; krb5_pa_data **padata; krb5_preauth_systems *pa_sys; void **pa_context; krb5_data *pa_e_data = NULL, *tmp_e_data = NULL; int pa_ok = 0, pa_found = 0; krb5_error_code saved_retval = 0; int use_saved_retval = 0; const char *emsg; krb5_authdata **tmp_authz_data = NULL; if (request->padata == 0) return 0; if (make_padata_context(context, padata_context) != 0) { return KRB5KRB_ERR_GENERIC; } #ifdef DEBUG krb5_klog_syslog (LOG_DEBUG, "checking padata"); #endif for (padata = request->padata; *padata; padata++) { #ifdef DEBUG krb5_klog_syslog (LOG_DEBUG, ".. pa_type 0x%x", (*padata)->pa_type); #endif if (find_pa_system((*padata)->pa_type, &pa_sys)) continue; if (find_pa_context(pa_sys, *padata_context, &pa_context)) continue; #ifdef DEBUG krb5_klog_syslog (LOG_DEBUG, ".. pa_type %s", pa_sys->name); #endif if (pa_sys->verify_padata == 0) continue; pa_found++; retval = pa_sys->verify_padata(context, client, req_pkt, request, enc_tkt_reply, *padata, get_entry_data, pa_sys->plugin_context, pa_context, &tmp_e_data, &tmp_authz_data); if (retval) { emsg = krb5_get_error_message (context, retval); krb5_klog_syslog (LOG_INFO, "preauth (%s) verify failure: %s", pa_sys->name, emsg); krb5_free_error_message (context, emsg); /* Ignore authorization data returned from modules that fail */ if (tmp_authz_data != NULL) { krb5_free_authdata(context, tmp_authz_data); tmp_authz_data = NULL; } if (pa_sys->flags & PA_REQUIRED) { /* free up any previous edata we might have been saving */ if (pa_e_data != NULL) krb5_free_data(context, pa_e_data); pa_e_data = tmp_e_data; tmp_e_data = NULL; use_saved_retval = 0; /* Make sure we use the current retval */ pa_ok = 0; break; } /* * We'll return edata from either the first PA_REQUIRED module * that fails, or the first non-PA_REQUIRED module that fails. * Hang on to edata from the first non-PA_REQUIRED module. * If we've already got one saved, simply discard this one. */ if (tmp_e_data != NULL) { if (pa_e_data == NULL) { /* save the first error code and e-data */ pa_e_data = tmp_e_data; tmp_e_data = NULL; saved_retval = retval; use_saved_retval = 1; } else { /* discard this extra e-data from non-PA_REQUIRED module */ krb5_free_data(context, tmp_e_data); tmp_e_data = NULL; } } } else { #ifdef DEBUG krb5_klog_syslog (LOG_DEBUG, ".. .. ok"); #endif /* Ignore any edata returned on success */ if (tmp_e_data != NULL) { krb5_free_data(context, tmp_e_data); tmp_e_data = NULL; } /* Add any authorization data to the ticket */ if (tmp_authz_data != NULL) { add_authorization_data(enc_tkt_reply, tmp_authz_data); free(tmp_authz_data); tmp_authz_data = NULL; } pa_ok = 1; if (pa_sys->flags & PA_SUFFICIENT) break; } } /* Don't bother copying and returning e-data on success */ if (pa_ok && pa_e_data != NULL) { krb5_free_data(context, pa_e_data); pa_e_data = NULL; } /* Return any e-data from the preauth that caused us to exit the loop */ if (pa_e_data != NULL) { e_data->data = malloc(pa_e_data->length); if (e_data->data == NULL) { krb5_free_data(context, pa_e_data); /* Solaris Kerberos */ return ENOMEM; } memcpy(e_data->data, pa_e_data->data, pa_e_data->length); e_data->length = pa_e_data->length; krb5_free_data(context, pa_e_data); pa_e_data = NULL; if (use_saved_retval != 0) retval = saved_retval; } if (pa_ok) return 0; /* pa system was not found, but principal doesn't require preauth */ if (!pa_found && !isflagset(client->attributes, KRB5_KDB_REQUIRES_PRE_AUTH) && !isflagset(client->attributes, KRB5_KDB_REQUIRES_HW_AUTH)) return 0; if (!pa_found) { emsg = krb5_get_error_message(context, retval); krb5_klog_syslog (LOG_INFO, "no valid preauth type found: %s", emsg); krb5_free_error_message(context, emsg); } /* The following switch statement allows us * to return some preauth system errors back to the client. */ switch(retval) { case KRB5KRB_AP_ERR_BAD_INTEGRITY: case KRB5KRB_AP_ERR_SKEW: case KRB5KDC_ERR_ETYPE_NOSUPP: /* rfc 4556 */ case KRB5KDC_ERR_CLIENT_NOT_TRUSTED: case KRB5KDC_ERR_INVALID_SIG: case KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED: case KRB5KDC_ERR_CANT_VERIFY_CERTIFICATE: case KRB5KDC_ERR_INVALID_CERTIFICATE: case KRB5KDC_ERR_REVOKED_CERTIFICATE: case KRB5KDC_ERR_REVOCATION_STATUS_UNKNOWN: case KRB5KDC_ERR_CLIENT_NAME_MISMATCH: case KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE: case KRB5KDC_ERR_DIGEST_IN_CERT_NOT_ACCEPTED: case KRB5KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED: case KRB5KDC_ERR_DIGEST_IN_SIGNED_DATA_NOT_ACCEPTED: case KRB5KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED: /* earlier drafts of what became rfc 4556 */ case KRB5KDC_ERR_CERTIFICATE_MISMATCH: case KRB5KDC_ERR_KDC_NOT_TRUSTED: case KRB5KDC_ERR_REVOCATION_STATUS_UNAVAILABLE: /* This value is shared with KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED. */ /* case KRB5KDC_ERR_KEY_TOO_WEAK: */ return retval; default: return KRB5KDC_ERR_PREAUTH_FAILED; } } /* * return_padata creates any necessary preauthentication * structures which should be returned by the KDC to the client */ krb5_error_code return_padata(krb5_context context, krb5_db_entry *client, krb5_data *req_pkt, krb5_kdc_req *request, krb5_kdc_rep *reply, krb5_key_data *client_key, krb5_keyblock *encrypting_key, void **padata_context) { krb5_error_code retval; krb5_pa_data ** padata; krb5_pa_data ** send_pa_list; krb5_pa_data ** send_pa; krb5_pa_data * pa = 0; krb5_preauth_systems * ap; int * pa_order; int * pa_type; int size = 0; void ** pa_context; krb5_boolean key_modified; krb5_keyblock original_key; if ((!*padata_context)&& (make_padata_context(context, padata_context) != 0)) { return KRB5KRB_ERR_GENERIC; } for (ap = preauth_systems; ap->type != -1; ap++) { if (ap->return_padata) size++; } if ((send_pa_list = malloc((size+1) * sizeof(krb5_pa_data *))) == NULL) return ENOMEM; if ((pa_order = malloc((size+1) * sizeof(int))) == NULL) { free(send_pa_list); return ENOMEM; } sort_pa_order(context, request, pa_order); retval = krb5_copy_keyblock_contents(context, encrypting_key, &original_key); if (retval) { free(send_pa_list); free(pa_order); return retval; } key_modified = FALSE; send_pa = send_pa_list; *send_pa = 0; for (pa_type = pa_order; *pa_type != -1; pa_type++) { ap = &preauth_systems[*pa_type]; if (!key_modified) if (original_key.enctype != encrypting_key->enctype) key_modified = TRUE; if (!key_modified) if (original_key.length != encrypting_key->length) key_modified = TRUE; if (!key_modified) if (memcmp(original_key.contents, encrypting_key->contents, original_key.length) != 0) key_modified = TRUE; if (key_modified && (ap->flags & PA_REPLACES_KEY)) continue; if (ap->return_padata == 0) continue; if (find_pa_context(ap, *padata_context, &pa_context)) continue; pa = 0; if (request->padata) { for (padata = request->padata; *padata; padata++) { if ((*padata)->pa_type == ap->type) { pa = *padata; break; } } } if ((retval = ap->return_padata(context, pa, client, req_pkt, request, reply, client_key, encrypting_key, send_pa, get_entry_data, ap->plugin_context, pa_context))) { goto cleanup; } if (*send_pa) send_pa++; *send_pa = 0; } retval = 0; if (send_pa_list[0]) { reply->padata = send_pa_list; send_pa_list = 0; } cleanup: if (send_pa_list) krb5_free_pa_data(context, send_pa_list); /* Solaris Kerberos */ krb5_free_keyblock_contents(context, &original_key); free(pa_order); return (retval); } static krb5_boolean enctype_requires_etype_info_2(krb5_enctype enctype) { switch(enctype) { case ENCTYPE_DES_CBC_CRC: case ENCTYPE_DES_CBC_MD4: case ENCTYPE_DES_CBC_MD5: case ENCTYPE_DES3_CBC_SHA1: case ENCTYPE_DES3_CBC_RAW: case ENCTYPE_ARCFOUR_HMAC: case ENCTYPE_ARCFOUR_HMAC_EXP : return 0; default: if (krb5_c_valid_enctype(enctype)) return 1; else return 0; } } static krb5_boolean request_contains_enctype (krb5_context context, const krb5_kdc_req *request, krb5_enctype enctype) { int i; for (i =0; i < request->nktypes; i++) if (request->ktype[i] == enctype) return 1; return 0; } static krb5_error_code verify_enc_timestamp(krb5_context context, krb5_db_entry *client, krb5_data *req_pkt, krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply, krb5_pa_data *pa, preauth_get_entry_data_proc ets_get_entry_data, void *pa_system_context, void **pa_request_context, krb5_data **e_data, krb5_authdata ***authz_data) { krb5_pa_enc_ts * pa_enc = 0; krb5_error_code retval; krb5_data scratch; krb5_data enc_ts_data; krb5_enc_data *enc_data = 0; krb5_keyblock key; krb5_key_data * client_key; krb5_int32 start; krb5_timestamp timenow; krb5_error_code decrypt_err = 0; (void) memset(&key, 0, sizeof(krb5_keyblock)); scratch.data = (char *) pa->contents; scratch.length = pa->length; enc_ts_data.data = 0; if ((retval = decode_krb5_enc_data(&scratch, &enc_data)) != 0) goto cleanup; enc_ts_data.length = enc_data->ciphertext.length; if ((enc_ts_data.data = (char *) malloc(enc_ts_data.length)) == NULL) goto cleanup; start = 0; decrypt_err = 0; while (1) { if ((retval = krb5_dbe_search_enctype(context, client, &start, enc_data->enctype, -1, 0, &client_key))) goto cleanup; if ((retval = krb5_dbekd_decrypt_key_data(context, &master_keyblock, client_key, &key, NULL))) goto cleanup; key.enctype = enc_data->enctype; retval = krb5_c_decrypt(context, &key, KRB5_KEYUSAGE_AS_REQ_PA_ENC_TS, 0, enc_data, &enc_ts_data); krb5_free_keyblock_contents(context, &key); if (retval == 0) break; else decrypt_err = retval; } if ((retval = decode_krb5_pa_enc_ts(&enc_ts_data, &pa_enc)) != 0) goto cleanup; if ((retval = krb5_timeofday(context, &timenow)) != 0) goto cleanup; if (labs(timenow - pa_enc->patimestamp) > context->clockskew) { retval = KRB5KRB_AP_ERR_SKEW; goto cleanup; } setflag(enc_tkt_reply->flags, TKT_FLG_PRE_AUTH); retval = 0; cleanup: if (enc_data) { krb5_free_data_contents(context, &enc_data->ciphertext); free(enc_data); } krb5_free_data_contents(context, &enc_ts_data); if (pa_enc) free(pa_enc); /* * If we get NO_MATCHING_KEY and decryption previously failed, and * we failed to find any other keys of the correct enctype after * that failed decryption, it probably means that the password was * incorrect. */ if (retval == KRB5_KDB_NO_MATCHING_KEY && decrypt_err != 0) retval = decrypt_err; return retval; } static krb5_error_code _make_etype_info_entry(krb5_context context, krb5_kdc_req *request, krb5_key_data *client_key, krb5_enctype etype, krb5_etype_info_entry **entry, int etype_info2) { krb5_data salt; krb5_etype_info_entry * tmp_entry; krb5_error_code retval; if ((tmp_entry = malloc(sizeof(krb5_etype_info_entry))) == NULL) return ENOMEM; salt.data = 0; tmp_entry->magic = KV5M_ETYPE_INFO_ENTRY; tmp_entry->etype = etype; tmp_entry->length = KRB5_ETYPE_NO_SALT; tmp_entry->salt = 0; tmp_entry->s2kparams.data = NULL; tmp_entry->s2kparams.length = 0; retval = get_salt_from_key(context, request->client, client_key, &salt); if (retval) goto fail; if (etype_info2 && client_key->key_data_ver > 1 && client_key->key_data_type[1] == KRB5_KDB_SALTTYPE_AFS3) { switch (etype) { case ENCTYPE_DES_CBC_CRC: case ENCTYPE_DES_CBC_MD4: case ENCTYPE_DES_CBC_MD5: tmp_entry->s2kparams.data = malloc(1); if (tmp_entry->s2kparams.data == NULL) { retval = ENOMEM; goto fail; } tmp_entry->s2kparams.length = 1; tmp_entry->s2kparams.data[0] = 1; break; default: break; } } if (salt.length >= 0) { tmp_entry->length = salt.length; tmp_entry->salt = (unsigned char *) salt.data; salt.data = 0; } *entry = tmp_entry; return 0; fail: if (tmp_entry) { if (tmp_entry->s2kparams.data) free(tmp_entry->s2kparams.data); free(tmp_entry); } if (salt.data) free(salt.data); return retval; } /* * This function returns the etype information for a particular * client, to be passed back in the preauth list in the KRB_ERROR * message. It supports generating both etype_info and etype_info2 * as most of the work is the same. */ static krb5_error_code etype_info_helper(krb5_context context, krb5_kdc_req *request, krb5_db_entry *client, krb5_db_entry *server, krb5_pa_data *pa_data, int etype_info2) { krb5_etype_info_entry ** entry = 0; krb5_key_data *client_key; krb5_error_code retval; krb5_data * scratch; krb5_enctype db_etype; int i = 0; int start = 0; int seen_des = 0; entry = malloc((client->n_key_data * 2 + 1) * sizeof(krb5_etype_info_entry *)); if (entry == NULL) return ENOMEM; entry[0] = NULL; while (1) { retval = krb5_dbe_search_enctype(context, client, &start, -1, -1, 0, &client_key); if (retval == KRB5_KDB_NO_MATCHING_KEY) break; if (retval) goto cleanup; db_etype = client_key->key_data_type[0]; if (db_etype == ENCTYPE_DES_CBC_MD4) db_etype = ENCTYPE_DES_CBC_MD5; if (request_contains_enctype(context, request, db_etype)) { assert(etype_info2 || !enctype_requires_etype_info_2(db_etype)); if ((retval = _make_etype_info_entry(context, request, client_key, db_etype, &entry[i], etype_info2)) != 0) { goto cleanup; } entry[i+1] = 0; i++; } /* * If there is a des key in the kdb, try the "similar" enctypes, * avoid duplicate entries. */ if (!seen_des) { switch (db_etype) { case ENCTYPE_DES_CBC_MD5: db_etype = ENCTYPE_DES_CBC_CRC; break; case ENCTYPE_DES_CBC_CRC: db_etype = ENCTYPE_DES_CBC_MD5; break; default: continue; } if (request_contains_enctype(context, request, db_etype)) { if ((retval = _make_etype_info_entry(context, request, client_key, db_etype, &entry[i], etype_info2)) != 0) { goto cleanup; } entry[i+1] = 0; i++; } seen_des++; } } if (etype_info2) retval = encode_krb5_etype_info2((krb5_etype_info_entry * const*) entry, &scratch); else retval = encode_krb5_etype_info((krb5_etype_info_entry * const*) entry, &scratch); if (retval) goto cleanup; pa_data->contents = (unsigned char *)scratch->data; pa_data->length = scratch->length; free(scratch); retval = 0; cleanup: if (entry) krb5_free_etype_info(context, entry); return retval; } static krb5_error_code get_etype_info(krb5_context context, krb5_kdc_req *request, krb5_db_entry *client, krb5_db_entry *server, preauth_get_entry_data_proc etype_get_entry_data, void *pa_system_context, krb5_pa_data *pa_data) { int i; for (i=0; i < request->nktypes; i++) { if (enctype_requires_etype_info_2(request->ktype[i])) return KRB5KDC_ERR_PADATA_TYPE_NOSUPP ;;;; /*Caller will * skip this * type*/ } return etype_info_helper(context, request, client, server, pa_data, 0); } static krb5_error_code get_etype_info2(krb5_context context, krb5_kdc_req *request, krb5_db_entry *client, krb5_db_entry *server, preauth_get_entry_data_proc etype_get_entry_data, void *pa_system_context, krb5_pa_data *pa_data) { return etype_info_helper( context, request, client, server, pa_data, 1); } static krb5_error_code etype_info_as_rep_helper(krb5_context context, krb5_pa_data * padata, krb5_db_entry *client, krb5_kdc_req *request, krb5_kdc_rep *reply, krb5_key_data *client_key, krb5_keyblock *encrypting_key, krb5_pa_data **send_pa, int etype_info2) { int i; krb5_error_code retval; krb5_pa_data *tmp_padata; krb5_etype_info_entry **entry = NULL; krb5_data *scratch = NULL; /* * Skip PA-ETYPE-INFO completely if AS-REQ lists any "newer" * enctypes. */ if (!etype_info2) { for (i = 0; i < request->nktypes; i++) { if (enctype_requires_etype_info_2(request->ktype[i])) { *send_pa = NULL; return 0; } } } tmp_padata = malloc( sizeof(krb5_pa_data)); if (tmp_padata == NULL) return ENOMEM; if (etype_info2) tmp_padata->pa_type = KRB5_PADATA_ETYPE_INFO2; else tmp_padata->pa_type = KRB5_PADATA_ETYPE_INFO; entry = malloc(2 * sizeof(krb5_etype_info_entry *)); if (entry == NULL) { retval = ENOMEM; goto cleanup; } entry[0] = NULL; entry[1] = NULL; retval = _make_etype_info_entry(context, request, client_key, encrypting_key->enctype, entry, etype_info2); if (retval) goto cleanup; if (etype_info2) retval = encode_krb5_etype_info2((krb5_etype_info_entry * const*) entry, &scratch); else retval = encode_krb5_etype_info((krb5_etype_info_entry * const*) entry, &scratch); if (retval) goto cleanup; tmp_padata->contents = (uchar_t *)scratch->data; tmp_padata->length = scratch->length; *send_pa = tmp_padata; /* For cleanup - we no longer own the contents of the krb5_data * only to pointer to the krb5_data */ scratch->data = 0; cleanup: if (entry) krb5_free_etype_info(context, entry); if (retval) { if (tmp_padata) free(tmp_padata); } if (scratch) krb5_free_data(context, scratch); return retval; } static krb5_error_code return_etype_info2(krb5_context context, krb5_pa_data * padata, krb5_db_entry *client, krb5_data *req_pkt, krb5_kdc_req *request, krb5_kdc_rep *reply, krb5_key_data *client_key, krb5_keyblock *encrypting_key, krb5_pa_data **send_pa, preauth_get_entry_data_proc etype_get_entry_data, void *pa_system_context, void **pa_request_context) { return etype_info_as_rep_helper(context, padata, client, request, reply, client_key, encrypting_key, send_pa, 1); } static krb5_error_code return_etype_info(krb5_context context, krb5_pa_data * padata, krb5_db_entry *client, krb5_data *req_pkt, krb5_kdc_req *request, krb5_kdc_rep *reply, krb5_key_data *client_key, krb5_keyblock *encrypting_key, krb5_pa_data **send_pa, preauth_get_entry_data_proc etypeget_entry_data, void *pa_system_context, void **pa_request_context) { return etype_info_as_rep_helper(context, padata, client, request, reply, client_key, encrypting_key, send_pa, 0); } static krb5_error_code return_pw_salt(krb5_context context, krb5_pa_data *in_padata, krb5_db_entry *client, krb5_data *req_pkt, krb5_kdc_req *request, krb5_kdc_rep *reply, krb5_key_data *client_key, krb5_keyblock *encrypting_key, krb5_pa_data **send_pa, preauth_get_entry_data_proc etype_get_entry_data, void *pa_system_context, void **pa_request_context) { krb5_error_code retval; krb5_pa_data * padata; krb5_data * scratch; krb5_data salt_data; int i; for (i = 0; i < request->nktypes; i++) { if (enctype_requires_etype_info_2(request->ktype[i])) return 0; } if (client_key->key_data_ver == 1 || client_key->key_data_type[1] == KRB5_KDB_SALTTYPE_NORMAL) return 0; if ((padata = malloc(sizeof(krb5_pa_data))) == NULL) return ENOMEM; padata->magic = KV5M_PA_DATA; padata->pa_type = KRB5_PADATA_PW_SALT; switch (client_key->key_data_type[1]) { case KRB5_KDB_SALTTYPE_V4: /* send an empty (V4) salt */ padata->contents = 0; padata->length = 0; break; case KRB5_KDB_SALTTYPE_NOREALM: if ((retval = krb5_principal2salt_norealm(kdc_context, request->client, &salt_data))) goto cleanup; padata->contents = (krb5_octet *)salt_data.data; padata->length = salt_data.length; break; case KRB5_KDB_SALTTYPE_AFS3: /* send an AFS style realm-based salt */ /* for now, just pass the realm back and let the client do the work. In the future, add a kdc configuration variable that specifies the old cell name. */ padata->pa_type = KRB5_PADATA_AFS3_SALT; /* it would be just like ONLYREALM, but we need to pass the 0 */ scratch = krb5_princ_realm(kdc_context, request->client); if ((padata->contents = malloc(scratch->length+1)) == NULL) { retval = ENOMEM; goto cleanup; } memcpy(padata->contents, scratch->data, scratch->length); padata->length = scratch->length+1; padata->contents[scratch->length] = 0; break; case KRB5_KDB_SALTTYPE_ONLYREALM: scratch = krb5_princ_realm(kdc_context, request->client); if ((padata->contents = malloc(scratch->length)) == NULL) { retval = ENOMEM; goto cleanup; } memcpy(padata->contents, scratch->data, scratch->length); padata->length = scratch->length; break; case KRB5_KDB_SALTTYPE_SPECIAL: if ((padata->contents = malloc(client_key->key_data_length[1])) == NULL) { retval = ENOMEM; goto cleanup; } memcpy(padata->contents, client_key->key_data_contents[1], client_key->key_data_length[1]); padata->length = client_key->key_data_length[1]; break; default: free(padata); return 0; } *send_pa = padata; return 0; cleanup: free(padata); return retval; } static krb5_error_code return_sam_data(krb5_context context, krb5_pa_data *in_padata, krb5_db_entry *client, krb5_data *req_pkt, krb5_kdc_req *request, krb5_kdc_rep *reply, krb5_key_data *client_key, krb5_keyblock *encrypting_key, krb5_pa_data **send_pa, preauth_get_entry_data_proc sam_get_entry_data, void *pa_system_context, void **pa_request_context) { krb5_error_code retval; krb5_data scratch; int i; krb5_sam_response *sr = 0; krb5_predicted_sam_response *psr = 0; if (in_padata == 0) return 0; /* * We start by doing the same thing verify_sam_response() does: * extract the psr from the padata (which is an sr). Nothing * here should generate errors! We've already successfully done * all this once. */ scratch.data = (char *) in_padata->contents; /* SUNWresync121 XXX */ scratch.length = in_padata->length; if ((retval = decode_krb5_sam_response(&scratch, &sr))) { com_err("krb5kdc", retval, gettext("return_sam_data(): decode_krb5_sam_response failed")); goto cleanup; } { krb5_enc_data tmpdata; tmpdata.enctype = ENCTYPE_UNKNOWN; tmpdata.ciphertext = sr->sam_track_id; scratch.length = tmpdata.ciphertext.length; if ((scratch.data = (char *) malloc(scratch.length)) == NULL) { retval = ENOMEM; goto cleanup; } if ((retval = krb5_c_decrypt(context, &psr_key, /* XXX */ 0, 0, &tmpdata, &scratch))) { com_err("krb5kdc", retval, gettext("return_sam_data(): decrypt track_id failed")); free(scratch.data); goto cleanup; } } if ((retval = decode_krb5_predicted_sam_response(&scratch, &psr))) { com_err("krb5kdc", retval, gettext( "return_sam_data(): decode_krb5_predicted_sam_response failed")); free(scratch.data); goto cleanup; } /* We could use sr->sam_flags, but it may be absent or altered. */ if (psr->sam_flags & KRB5_SAM_MUST_PK_ENCRYPT_SAD) { com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED, gettext("Unsupported SAM flag must-pk-encrypt-sad")); goto cleanup; } if (psr->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD) { /* No key munging */ goto cleanup; } if (psr->sam_flags & KRB5_SAM_USE_SAD_AS_KEY) { /* Use sam_key instead of client key */ krb5_free_keyblock_contents(context, encrypting_key); krb5_copy_keyblock_contents(context, &psr->sam_key, encrypting_key); /* XXX Attach a useful pa_data */ goto cleanup; } /* Otherwise (no flags set), we XOR the keys */ /* XXX The passwords-04 draft is underspecified here wrt different key types. We will do what I hope to get into the -05 draft. */ { krb5_octet *p = encrypting_key->contents; krb5_octet *q = psr->sam_key.contents; int length = ((encrypting_key->length < psr->sam_key.length) ? encrypting_key->length : psr->sam_key.length); for (i = 0; i < length; i++) p[i] ^= q[i]; } /* Post-mixing key correction */ switch (encrypting_key->enctype) { case ENCTYPE_DES_CBC_CRC: case ENCTYPE_DES_CBC_MD4: case ENCTYPE_DES_CBC_MD5: case ENCTYPE_DES_CBC_RAW: mit_des_fixup_key_parity(encrypting_key->contents); if (mit_des_is_weak_key(encrypting_key->contents)) ((krb5_octet *) encrypting_key->contents)[7] ^= 0xf0; break; /* XXX case ENCTYPE_DES3_CBC_MD5: listed in 1510bis-04 draft */ case ENCTYPE_DES3_CBC_SHA: /* XXX deprecated? */ case ENCTYPE_DES3_CBC_RAW: case ENCTYPE_DES3_CBC_SHA1: for (i = 0; i < 3; i++) { mit_des_fixup_key_parity(encrypting_key->contents + i * 8); if (mit_des_is_weak_key(encrypting_key->contents + i * 8)) ((krb5_octet *) encrypting_key->contents)[7 + i * 8] ^= 0xf0; } break; default: com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED, gettext("Unimplemented keytype for SAM key mixing")); goto cleanup; } /* XXX Attach a useful pa_data */ cleanup: if (sr) krb5_free_sam_response(context, sr); if (psr) krb5_free_predicted_sam_response(context, psr); return retval; } static struct { char* name; int sam_type; } *sam_ptr, sam_inst_map[] = { #if 0 /* SUNWresync121 - unsupported hardware and kdc.log annoyance */ { "SNK4", PA_SAM_TYPE_DIGI_PATH, }, { "SECURID", PA_SAM_TYPE_SECURID, }, { "GRAIL", PA_SAM_TYPE_GRAIL, }, #endif { 0, 0 }, }; static krb5_error_code get_sam_edata(krb5_context context, krb5_kdc_req *request, krb5_db_entry *client, krb5_db_entry *server, preauth_get_entry_data_proc sam_get_entry_data, void *pa_system_context, krb5_pa_data *pa_data) { krb5_error_code retval; krb5_sam_challenge sc; krb5_predicted_sam_response psr; krb5_data * scratch; krb5_keyblock encrypting_key; char response[9]; char inputblock[8]; krb5_data predict_response; (void) memset(&encrypting_key, 0, sizeof(krb5_keyblock)); (void) memset(&sc, 0, sizeof(sc)); (void) memset(&psr, 0, sizeof(psr)); /* Given the client name we can figure out what type of preauth they need. The spec is currently for querying the database for names that match the types of preauth used. Later we should make this mapping show up in kdc.conf. In the meantime, we hardcode the following: /SNK4 -- Digital Pathways SNK/4 preauth. /GRAIL -- experimental preauth The first one found is used. See sam_inst_map above. For SNK4 in particular, the key in the database is the key for the device; kadmin needs a special interface for it. */ { int npr = 1; krb5_boolean more; krb5_db_entry assoc; krb5_key_data *assoc_key; krb5_principal newp; int probeslot; sc.sam_type = 0; retval = krb5_copy_principal(kdc_context, request->client, &newp); if (retval) { com_err(gettext("krb5kdc"), retval, gettext("copying client name for preauth probe")); return retval; } probeslot = krb5_princ_size(context, newp)++; krb5_princ_name(kdc_context, newp) = realloc(krb5_princ_name(kdc_context, newp), krb5_princ_size(context, newp) * sizeof(krb5_data)); for(sam_ptr = sam_inst_map; sam_ptr->name; sam_ptr++) { krb5_princ_component(kdc_context,newp,probeslot)->data = sam_ptr->name; krb5_princ_component(kdc_context,newp,probeslot)->length = strlen(sam_ptr->name); npr = 1; retval = krb5_db_get_principal(kdc_context, newp, &assoc, &npr, (uint *)&more); if(!retval && npr) { sc.sam_type = sam_ptr->sam_type; break; } } krb5_princ_component(kdc_context,newp,probeslot)->data = 0; krb5_princ_component(kdc_context,newp,probeslot)->length = 0; krb5_princ_size(context, newp)--; krb5_free_principal(kdc_context, newp); /* if sc.sam_type is set, it worked */ if (sc.sam_type) { /* so use assoc to get the key out! */ { /* here's what do_tgs_req does */ retval = krb5_dbe_find_enctype(kdc_context, &assoc, ENCTYPE_DES_CBC_RAW, KRB5_KDB_SALTTYPE_NORMAL, 0, /* Get highest kvno */ &assoc_key); if (retval) { char *sname; krb5_unparse_name(kdc_context, request->client, &sname); com_err(gettext("krb5kdc"), retval, gettext("snk4 finding the enctype and key <%s>"), sname); free(sname); return retval; } /* convert server.key into a real key */ retval = krb5_dbekd_decrypt_key_data(kdc_context, &master_keyblock, assoc_key, &encrypting_key, NULL); if (retval) { com_err(gettext("krb5kdc"), retval, gettext("snk4 pulling out key entry")); return retval; } /* now we can use encrypting_key... */ } } else { /* SAM is not an option - so don't return as hint */ return KRB5_PREAUTH_BAD_TYPE; } } sc.magic = KV5M_SAM_CHALLENGE; psr.sam_flags = sc.sam_flags = KRB5_SAM_USE_SAD_AS_KEY; /* Replay prevention */ if ((retval = krb5_copy_principal(context, request->client, &psr.client))) return retval; #ifdef USE_RCACHE if ((retval = krb5_us_timeofday(context, &psr.stime, &psr.susec))) return retval; #endif /* USE_RCACHE */ switch (sc.sam_type) { case PA_SAM_TYPE_GRAIL: sc.sam_type_name.data = "Experimental System"; sc.sam_type_name.length = strlen(sc.sam_type_name.data); sc.sam_challenge_label.data = "experimental challenge label"; sc.sam_challenge_label.length = strlen(sc.sam_challenge_label.data); sc.sam_challenge.data = "12345"; sc.sam_challenge.length = strlen(sc.sam_challenge.data); #if 0 /* Enable this to test "normal" (no flags set) mode. */ psr.sam_flags = sc.sam_flags = 0; #endif psr.magic = KV5M_PREDICTED_SAM_RESPONSE; /* string2key on sc.sam_challenge goes in here */ /* eblock is just to set the enctype */ { const krb5_enctype type = ENCTYPE_DES_CBC_MD5; if ((retval = krb5_c_string_to_key(context, type, &sc.sam_challenge, 0 /* salt */, &psr.sam_key))) goto cleanup; if ((retval = encode_krb5_predicted_sam_response(&psr, &scratch))) goto cleanup; { size_t enclen; krb5_enc_data tmpdata; if ((retval = krb5_c_encrypt_length(context, psr_key.enctype, scratch->length, &enclen))) goto cleanup; if ((tmpdata.ciphertext.data = (char *) malloc(enclen)) == NULL) { retval = ENOMEM; goto cleanup; } tmpdata.ciphertext.length = enclen; if ((retval = krb5_c_encrypt(context, &psr_key, /* XXX */ 0, 0, scratch, &tmpdata))) goto cleanup; sc.sam_track_id = tmpdata.ciphertext; } } sc.sam_response_prompt.data = "response prompt"; sc.sam_response_prompt.length = strlen(sc.sam_response_prompt.data); sc.sam_pk_for_sad.length = 0; sc.sam_nonce = 0; /* Generate checksum */ /*krb5_checksum_size(context, ctype)*/ /*krb5_calculate_checksum(context,ctype,in,in_length,seed, seed_length,outcksum) */ /*krb5_verify_checksum(context,ctype,cksum,in,in_length,seed, seed_length) */ #if 0 /* XXX a) glue appears broken; b) this gives up the SAD */ sc.sam_cksum.contents = (krb5_octet *) malloc(krb5_checksum_size(context, CKSUMTYPE_RSA_MD5_DES)); if (sc.sam_cksum.contents == NULL) return(ENOMEM); retval = krb5_calculate_checksum(context, CKSUMTYPE_RSA_MD5_DES, sc.sam_challenge.data, sc.sam_challenge.length, psr.sam_key.contents, /* key */ psr.sam_key.length, /* key length */ &sc.sam_cksum); if (retval) { free(sc.sam_cksum.contents); return(retval); } #endif /* 0 */ retval = encode_krb5_sam_challenge(&sc, &scratch); if (retval) goto cleanup; pa_data->magic = KV5M_PA_DATA; pa_data->pa_type = KRB5_PADATA_SAM_CHALLENGE; pa_data->contents = (unsigned char *) scratch->data; pa_data->length = scratch->length; retval = 0; break; case PA_SAM_TYPE_DIGI_PATH: sc.sam_type_name.data = "Digital Pathways"; sc.sam_type_name.length = strlen(sc.sam_type_name.data); #if 1 sc.sam_challenge_label.data = "Enter the following on your keypad"; sc.sam_challenge_label.length = strlen(sc.sam_challenge_label.data); #endif /* generate digit string, take it mod 1000000 (six digits.) */ { int j; krb5_keyblock session_key; char outputblock[8]; int i; (void) memset(&session_key, 0, sizeof(krb5_keyblock)); (void) memset(inputblock, 0, 8); retval = krb5_c_make_random_key(kdc_context, ENCTYPE_DES_CBC_CRC, &session_key); if (retval) { /* random key failed */ com_err(gettext("krb5kdc"), retval, gettext("generating random challenge for preauth")); return retval; } /* now session_key has a key which we can pick bits out of */ /* we need six decimal digits. Grab 6 bytes, div 2, mod 10 each. */ if (session_key.length != 8) { retval = KRB5KDC_ERR_ETYPE_NOSUPP, com_err(gettext("krb5kdc"), retval = KRB5KDC_ERR_ETYPE_NOSUPP, gettext("keytype didn't match code expectations")); return retval; } for(i = 0; i<6; i++) { inputblock[i] = '0' + ((session_key.contents[i]/2) % 10); } if (session_key.contents) krb5_free_keyblock_contents(kdc_context, &session_key); /* retval = krb5_finish_key(kdc_context, &eblock); */ /* now we have inputblock containing the 8 byte input to DES... */ sc.sam_challenge.data = inputblock; sc.sam_challenge.length = 6; encrypting_key.enctype = ENCTYPE_DES_CBC_RAW; if (retval) { com_err(gettext("krb5kdc"), retval, gettext("snk4 processing key")); } { krb5_data plain; krb5_enc_data cipher; plain.length = 8; plain.data = inputblock; /* XXX I know this is enough because of the fixed raw enctype. if it's not, the underlying code will return a reasonable error, which should never happen */ cipher.ciphertext.length = 8; cipher.ciphertext.data = outputblock; if ((retval = krb5_c_encrypt(kdc_context, &encrypting_key, /* XXX */ 0, 0, &plain, &cipher))) { com_err(gettext("krb5kdc"), retval, gettext("snk4 response generation failed")); return retval; } } /* now output block is the raw bits of the response; convert it to display form */ for (j=0; j<4; j++) { char n[2]; int k; n[0] = outputblock[j] & 0xf; n[1] = (outputblock[j]>>4) & 0xf; for (k=0; k<2; k++) { if(n[k] > 9) n[k] = ((n[k]-1)>>2); /* This is equivalent to: if(n[k]>=0xa && n[k]<=0xc) n[k] = 2; if(n[k]>=0xd && n[k]<=0xf) n[k] = 3; */ } /* for v4, we keygen: *(j+(char*)&key1) = (n[1]<<4) | n[0]; */ /* for v5, we just generate a string */ response[2*j+0] = '0' + n[1]; response[2*j+1] = '0' + n[0]; /* and now, response has what we work with. */ } response[8] = 0; predict_response.data = response; predict_response.length = 8; #if 0 /* for debugging, hack the output too! */ sc.sam_challenge_label.data = response; sc.sam_challenge_label.length = strlen(sc.sam_challenge_label.data); #endif } psr.magic = KV5M_PREDICTED_SAM_RESPONSE; /* string2key on sc.sam_challenge goes in here */ /* eblock is just to set the enctype */ { retval = krb5_c_string_to_key(context, ENCTYPE_DES_CBC_MD5, &predict_response, 0 /* salt */, &psr.sam_key); if (retval) goto cleanup; retval = encode_krb5_predicted_sam_response(&psr, &scratch); if (retval) goto cleanup; { size_t enclen; krb5_enc_data tmpdata; if ((retval = krb5_c_encrypt_length(context, psr_key.enctype, scratch->length, &enclen))) goto cleanup; if ((tmpdata.ciphertext.data = (char *) malloc(enclen)) == NULL) { retval = ENOMEM; goto cleanup; } tmpdata.ciphertext.length = enclen; if ((retval = krb5_c_encrypt(context, &psr_key, /* XXX */ 0, 0, scratch, &tmpdata))) goto cleanup; sc.sam_track_id = tmpdata.ciphertext; } if (retval) goto cleanup; } sc.sam_response_prompt.data = "Enter the displayed response"; sc.sam_response_prompt.length = strlen(sc.sam_response_prompt.data); sc.sam_pk_for_sad.length = 0; sc.sam_nonce = 0; /* Generate checksum */ /*krb5_checksum_size(context, ctype)*/ /*krb5_calculate_checksum(context,ctype,in,in_length,seed, seed_length,outcksum) */ /*krb5_verify_checksum(context,ctype,cksum,in,in_length,seed, seed_length) */ #if 0 /* XXX a) glue appears broken; b) this gives up the SAD */ sc.sam_cksum.contents = (krb5_octet *) malloc(krb5_checksum_size(context, CKSUMTYPE_RSA_MD5_DES)); if (sc.sam_cksum.contents == NULL) return(ENOMEM); retval = krb5_calculate_checksum(context, CKSUMTYPE_RSA_MD5_DES, sc.sam_challenge.data, sc.sam_challenge.length, psr.sam_key.contents, /* key */ psr.sam_key.length, /* key length */ &sc.sam_cksum); if (retval) { free(sc.sam_cksum.contents); return(retval); } #endif /* 0 */ retval = encode_krb5_sam_challenge(&sc, &scratch); if (retval) goto cleanup; pa_data->magic = KV5M_PA_DATA; pa_data->pa_type = KRB5_PADATA_SAM_CHALLENGE; pa_data->contents = (unsigned char *) scratch->data; pa_data->length = scratch->length; retval = 0; break; } cleanup: krb5_free_keyblock_contents(context, &encrypting_key); return retval; } static krb5_error_code verify_sam_response(krb5_context context, krb5_db_entry *client, krb5_data *req_pkt, krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply, krb5_pa_data *pa, preauth_get_entry_data_proc sam_get_entry_data, void *pa_system_context, void **pa_request_context, krb5_data **e_data, krb5_authdata ***authz_data) { krb5_error_code retval; krb5_data scratch; krb5_sam_response *sr = 0; krb5_predicted_sam_response *psr = 0; krb5_enc_sam_response_enc *esre = 0; krb5_timestamp timenow; char *princ_req = 0, *princ_psr = 0; scratch.data = (char *) pa->contents; scratch.length = pa->length; if ((retval = decode_krb5_sam_response(&scratch, &sr))) { scratch.data = 0; com_err("krb5kdc", retval, gettext("decode_krb5_sam_response failed")); goto cleanup; } /* XXX We can only handle the challenge/response model of SAM. See passwords-04, par 4.1, 4.2 */ { krb5_enc_data tmpdata; tmpdata.enctype = ENCTYPE_UNKNOWN; tmpdata.ciphertext = sr->sam_track_id; scratch.length = tmpdata.ciphertext.length; if ((scratch.data = (char *) malloc(scratch.length)) == NULL) { retval = ENOMEM; goto cleanup; } if ((retval = krb5_c_decrypt(context, &psr_key, /* XXX */ 0, 0, &tmpdata, &scratch))) { com_err(gettext("krb5kdc"), retval, gettext("decrypt track_id failed")); goto cleanup; } } if ((retval = decode_krb5_predicted_sam_response(&scratch, &psr))) { com_err(gettext("krb5kdc"), retval, gettext("decode_krb5_predicted_sam_response failed -- replay attack?")); goto cleanup; } /* Replay detection */ if ((retval = krb5_unparse_name(context, request->client, &princ_req))) goto cleanup; if ((retval = krb5_unparse_name(context, psr->client, &princ_psr))) goto cleanup; if (strcmp(princ_req, princ_psr) != 0) { com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED, gettext("Principal mismatch in SAM psr! -- replay attack?")); goto cleanup; } if ((retval = krb5_timeofday(context, &timenow))) goto cleanup; #ifdef USE_RCACHE { krb5_donot_replay rep; extern krb5_deltat rc_lifetime; /* * Verify this response came back in a timely manner. * We do this b/c otherwise very old (expunged from the rcache) * psr's would be able to be replayed. */ if (timenow - psr->stime > rc_lifetime) { com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED, gettext("SAM psr came back too late! -- replay attack?")); goto cleanup; } /* Now check the replay cache. */ rep.client = princ_psr; rep.server = "SAM/rc"; /* Should not match any principal name. */ rep.ctime = psr->stime; rep.cusec = psr->susec; retval = krb5_rc_store(kdc_context, kdc_rcache, &rep); if (retval) { com_err("krb5kdc", retval, gettext("SAM psr replay attack!")); goto cleanup; } } #endif /* USE_RCACHE */ { free(scratch.data); scratch.length = sr->sam_enc_nonce_or_ts.ciphertext.length; if ((scratch.data = (char *) malloc(scratch.length)) == NULL) { retval = ENOMEM; goto cleanup; } if ((retval = krb5_c_decrypt(context, &psr->sam_key, /* XXX */ 0, 0, &sr->sam_enc_nonce_or_ts, &scratch))) { com_err("krb5kdc", retval, gettext("decrypt nonce_or_ts failed")); goto cleanup; } } if ((retval = decode_krb5_enc_sam_response_enc(&scratch, &esre))) { com_err("krb5kdc", retval, gettext("decode_krb5_enc_sam_response_enc failed")); goto cleanup; } if (esre->sam_timestamp != sr->sam_patimestamp) { retval = KRB5KDC_ERR_PREAUTH_FAILED; goto cleanup; } if (labs(timenow - sr->sam_patimestamp) > context->clockskew) { retval = KRB5KRB_AP_ERR_SKEW; goto cleanup; } setflag(enc_tkt_reply->flags, TKT_FLG_HW_AUTH); cleanup: if (retval) com_err(gettext("krb5kdc"), retval, gettext("sam verify failure")); if (scratch.data) free(scratch.data); if (sr) free(sr); if (psr) free(psr); if (esre) free(esre); if (princ_psr) free(princ_psr); if (princ_req) free(princ_req); return retval; } /* * kdc/kdc_util.c * * Copyright 1990,1991 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 the KDC implementation. */ #include "k5-int.h" #include "kdc_util.h" #include "extern.h" #include #include #include #include "adm.h" #include "adm_proto.h" #include #ifdef USE_RCACHE static char *kdc_current_rcname = (char *) NULL; krb5_deltat rc_lifetime; /* See kdc_initialize_rcache() */ #endif #ifdef USE_RCACHE /* * initialize the replay cache. */ krb5_error_code kdc_initialize_rcache(krb5_context kcontext, char *rcache_name) { krb5_error_code retval; char *rcname; char *sname; rcname = (rcache_name) ? rcache_name : kdc_current_rcname; /* rc_lifetime used elsewhere to verify we're not */ /* replaying really old data */ rc_lifetime = kcontext->clockskew; if (!rcname) rcname = KDCRCACHE; if (!(retval = krb5_rc_resolve_full(kcontext, &kdc_rcache, rcname))) { /* Recover or initialize the replay cache */ if (!(retval = krb5_rc_recover(kcontext, kdc_rcache)) || !(retval = krb5_rc_initialize(kcontext, kdc_rcache, kcontext->clockskew)) ) { /* Expunge the replay cache */ if (!(retval = krb5_rc_expunge(kcontext, kdc_rcache))) { sname = kdc_current_rcname; kdc_current_rcname = strdup(rcname); if (sname) free(sname); } } if (retval) krb5_rc_close(kcontext, kdc_rcache); } return(retval); } #endif /* * concatenate first two authdata arrays, returning an allocated replacement. * The replacement should be freed with krb5_free_authdata(). */ krb5_error_code concat_authorization_data(krb5_authdata **first, krb5_authdata **second, krb5_authdata ***output) { register int i, j; register krb5_authdata **ptr, **retdata; /* count up the entries */ i = 0; if (first) for (ptr = first; *ptr; ptr++) i++; if (second) for (ptr = second; *ptr; ptr++) i++; retdata = (krb5_authdata **)malloc((i+1)*sizeof(*retdata)); if (!retdata) return ENOMEM; retdata[i] = 0; /* null-terminated array */ for (i = 0, j = 0, ptr = first; j < 2 ; ptr = second, j++) while (ptr && *ptr) { /* now walk & copy */ retdata[i] = (krb5_authdata *)malloc(sizeof(*retdata[i])); if (!retdata[i]) { krb5_free_authdata(kdc_context, retdata); return ENOMEM; } *retdata[i] = **ptr; if (!(retdata[i]->contents = (krb5_octet *)malloc(retdata[i]->length))) { free((char *)retdata[i]); retdata[i] = 0; krb5_free_authdata(kdc_context, retdata); return ENOMEM; } memcpy((char *) retdata[i]->contents, (char *)(*ptr)->contents, retdata[i]->length); ptr++; i++; } *output = retdata; return 0; } krb5_boolean realm_compare(krb5_principal princ1, krb5_principal princ2) { krb5_data *realm1 = krb5_princ_realm(kdc_context, princ1); krb5_data *realm2 = krb5_princ_realm(kdc_context, princ2); return((realm1->length == realm2->length) && !memcmp(realm1->data, realm2->data, realm1->length)); } /* * Returns TRUE if the kerberos principal is the name of a Kerberos ticket * service. */ krb5_boolean krb5_is_tgs_principal(krb5_principal principal) { if ((krb5_princ_size(kdc_context, principal) > 0) && (krb5_princ_component(kdc_context, principal, 0)->length == KRB5_TGS_NAME_SIZE) && (!memcmp(krb5_princ_component(kdc_context, principal, 0)->data, KRB5_TGS_NAME, KRB5_TGS_NAME_SIZE))) return TRUE; return FALSE; } /* * given authentication data (provides seed for checksum), verify checksum * for source data. */ static krb5_error_code comp_cksum(krb5_context kcontext, krb5_data *source, krb5_ticket *ticket, krb5_checksum *his_cksum) { krb5_error_code retval; krb5_boolean valid; if (!krb5_c_valid_cksumtype(his_cksum->checksum_type)) return KRB5KDC_ERR_SUMTYPE_NOSUPP; /* must be collision proof */ if (!krb5_c_is_coll_proof_cksum(his_cksum->checksum_type)) return KRB5KRB_AP_ERR_INAPP_CKSUM; /* verify checksum */ if ((retval = krb5_c_verify_checksum(kcontext, ticket->enc_part2->session, KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM, source, his_cksum, &valid))) return(retval); if (!valid) return(KRB5KRB_AP_ERR_BAD_INTEGRITY); return(0); } krb5_error_code kdc_process_tgs_req(krb5_kdc_req *request, const krb5_fulladdr *from, krb5_data *pkt, krb5_ticket **ticket, krb5_keyblock **subkey) { krb5_pa_data ** tmppa; krb5_ap_req * apreq; krb5_error_code retval; krb5_data scratch1; krb5_data * scratch = NULL; krb5_boolean foreign_server = FALSE; krb5_auth_context auth_context = NULL; krb5_authenticator * authenticator = NULL; krb5_checksum * his_cksum = NULL; /* krb5_keyblock * key = NULL;*/ /* krb5_kvno kvno = 0;*/ if (!request->padata) return KRB5KDC_ERR_PADATA_TYPE_NOSUPP; for (tmppa = request->padata; *tmppa; tmppa++) { if ((*tmppa)->pa_type == KRB5_PADATA_AP_REQ) break; } if (!*tmppa) /* cannot find any AP_REQ */ return KRB5KDC_ERR_PADATA_TYPE_NOSUPP; scratch1.length = (*tmppa)->length; scratch1.data = (char *)(*tmppa)->contents; if ((retval = decode_krb5_ap_req(&scratch1, &apreq))) return retval; if (isflagset(apreq->ap_options, AP_OPTS_USE_SESSION_KEY) || isflagset(apreq->ap_options, AP_OPTS_MUTUAL_REQUIRED)) { krb5_klog_syslog(LOG_INFO, "TGS_REQ: SESSION KEY or MUTUAL"); retval = KRB5KDC_ERR_POLICY; goto cleanup; } /* If the "server" principal in the ticket is not something in the local realm, then we must refuse to service the request if the client claims to be from the local realm. If we don't do this, then some other realm's nasty KDC can claim to be authenticating a client from our realm, and we'll give out tickets concurring with it! we set a flag here for checking below. */ if ((krb5_princ_realm(kdc_context, apreq->ticket->server)->length != krb5_princ_realm(kdc_context, tgs_server)->length) || memcmp(krb5_princ_realm(kdc_context, apreq->ticket->server)->data, krb5_princ_realm(kdc_context, tgs_server)->data, krb5_princ_realm(kdc_context, tgs_server)->length)) foreign_server = TRUE; if ((retval = krb5_auth_con_init(kdc_context, &auth_context))) goto cleanup; if ((retval = krb5_auth_con_setaddrs(kdc_context, auth_context, NULL, from->address)) ) goto cleanup_auth_context; #ifdef USE_RCACHE if ((retval = krb5_auth_con_setrcache(kdc_context, auth_context, kdc_rcache))) goto cleanup_auth_context; #endif /* if ((retval = kdc_get_server_key(apreq->ticket, &key, &kvno))) goto cleanup_auth_context; */ /* * XXX This is currently wrong but to fix it will require making a * new keytab for groveling over the kdb. */ /* retval = krb5_auth_con_setuseruserkey(kdc_context, auth_context, key); krb5_free_keyblock(kdc_context, key); if (retval) goto cleanup_auth_context; */ if ((retval = krb5_rd_req_decoded_anyflag(kdc_context, &auth_context, apreq, apreq->ticket->server, kdc_active_realm->realm_keytab, NULL, ticket))) { #ifdef USE_RCACHE /* * I'm not so sure that this is right, but it's better than nothing * at all. * * If we choke in the rd_req because of the replay cache, then attempt * to reinitialize the replay cache because somebody could have deleted * it from underneath us (e.g. a cron job) */ if ((retval == KRB5_RC_IO_IO) || (retval == KRB5_RC_IO_UNKNOWN)) { (void) krb5_rc_close(kdc_context, kdc_rcache); kdc_rcache = (krb5_rcache) NULL; if (!(retval = kdc_initialize_rcache(kdc_context, (char *) NULL))) { if ((retval = krb5_auth_con_setrcache(kdc_context, auth_context, kdc_rcache)) || (retval = krb5_rd_req_decoded_anyflag(kdc_context, &auth_context, apreq, apreq->ticket->server, kdc_active_realm->realm_keytab, NULL, ticket)) ) goto cleanup_auth_context; } } else goto cleanup_auth_context; #else goto cleanup_auth_context; #endif } /* "invalid flag" tickets can must be used to validate */ if (isflagset((*ticket)->enc_part2->flags, TKT_FLG_INVALID) && !isflagset(request->kdc_options, KDC_OPT_VALIDATE)) { retval = KRB5KRB_AP_ERR_TKT_INVALID; goto cleanup_auth_context; } if ((retval = krb5_auth_con_getrecvsubkey(kdc_context, auth_context, subkey))) goto cleanup_auth_context; if ((retval = krb5_auth_con_getauthenticator(kdc_context, auth_context, &authenticator))) goto cleanup_auth_context; /* Check for a checksum */ if (!(his_cksum = authenticator->checksum)) { retval = KRB5KRB_AP_ERR_INAPP_CKSUM; goto cleanup_authenticator; } /* make sure the client is of proper lineage (see above) */ if (foreign_server) { krb5_data *tkt_realm = krb5_princ_realm(kdc_context, (*ticket)->enc_part2->client); krb5_data *tgs_realm = krb5_princ_realm(kdc_context, tgs_server); if (tkt_realm->length == tgs_realm->length && !memcmp(tkt_realm->data, tgs_realm->data, tgs_realm->length)) { /* someone in a foreign realm claiming to be local */ krb5_klog_syslog(LOG_INFO, "PROCESS_TGS: failed lineage check"); retval = KRB5KDC_ERR_POLICY; goto cleanup_authenticator; } } /* * Check application checksum vs. tgs request * * We try checksumming the req-body two different ways: first we * try reaching into the raw asn.1 stream (if available), and * checksum that directly; if that fails, then we try encoding * using our local asn.1 library. */ if (pkt && (fetch_asn1_field((unsigned char *) pkt->data, 1, 4, &scratch1) >= 0)) { if (comp_cksum(kdc_context, &scratch1, *ticket, his_cksum)) { if (!(retval = encode_krb5_kdc_req_body(request, &scratch))) retval = comp_cksum(kdc_context, scratch, *ticket, his_cksum); krb5_free_data(kdc_context, scratch); } } cleanup_authenticator: krb5_free_authenticator(kdc_context, authenticator); cleanup_auth_context: /* We do not want the free of the auth_context to close the rcache */ #ifdef USE_RCACHE (void) krb5_auth_con_setrcache(kdc_context, auth_context, 0); #endif krb5_auth_con_free(kdc_context, auth_context); cleanup: krb5_free_ap_req(kdc_context, apreq); return retval; } /* XXX This function should no longer be necessary. * The KDC should take the keytab associated with the realm and pass that to * the krb5_rd_req_decode(). --proven * * It's actually still used by do_tgs_req() for u2u auth, and not too * much else. -- tlyu */ krb5_error_code kdc_get_server_key(krb5_ticket *ticket, krb5_keyblock **key, krb5_kvno *kvno) { krb5_error_code retval; krb5_db_entry server; krb5_boolean more; int nprincs; krb5_key_data * server_key; nprincs = 1; if ((retval = krb5_db_get_principal(kdc_context, ticket->server, &server, &nprincs, &more))) { return(retval); } if (more) { krb5_db_free_principal(kdc_context, &server, nprincs); return(KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE); } else if (nprincs != 1) { char *sname; krb5_db_free_principal(kdc_context, &server, nprincs); if (!krb5_unparse_name(kdc_context, ticket->server, &sname)) { limit_string(sname); krb5_klog_syslog(LOG_ERR,"TGS_REQ: UNKNOWN SERVER: server='%s'", sname); free(sname); } return(KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN); } retval = krb5_dbe_find_enctype(kdc_context, &server, ticket->enc_part.enctype, -1, ticket->enc_part.kvno, &server_key); if (retval) goto errout; if (!server_key) { retval = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; goto errout; } *kvno = server_key->key_data_kvno; if ((*key = (krb5_keyblock *)malloc(sizeof **key))) { retval = krb5_dbekd_decrypt_key_data(kdc_context, &master_keyblock, server_key, *key, NULL); } else retval = ENOMEM; errout: krb5_db_free_principal(kdc_context, &server, nprincs); return retval; } /* This probably wants to be updated if you support last_req stuff */ static krb5_last_req_entry nolrentry = { KV5M_LAST_REQ_ENTRY, KRB5_LRQ_NONE, 0 }; static krb5_last_req_entry *nolrarray[] = { &nolrentry, 0 }; krb5_error_code fetch_last_req_info(krb5_db_entry *dbentry, krb5_last_req_entry ***lrentry) { *lrentry = nolrarray; return 0; } /* XXX! This is a temporary place-holder */ krb5_error_code check_hot_list(krb5_ticket *ticket) { return 0; } #define MAX_REALM_LN 500 /* * subrealm - determine if r2 is a subrealm of r1 * * SUBREALM takes two realms, r1 and r2, and * determines if r2 is a subrealm of r1. * r2 is a subrealm of r1 if (r1 is a prefix * of r2 AND r1 and r2 begin with a /) or if * (r1 is a suffix of r2 and neither r1 nor r2 * begin with a /). * * RETURNS: If r2 is a subrealm, and r1 is a prefix, the number * of characters in the suffix of r2 is returned as a * negative number. * * If r2 is a subrealm, and r1 is a suffix, the number * of characters in the prefix of r2 is returned as a * positive number. * * If r2 is not a subrealm, SUBREALM returns 0. */ static int subrealm(char *r1, char *r2) { size_t l1,l2; l1 = strlen(r1); l2 = strlen(r2); if(l2 <= l1) return(0); if((*r1 == '/') && (*r2 == '/') && (strncmp(r1,r2,l1) == 0)) return(l1-l2); if((*r1 != '/') && (*r2 != '/') && (strncmp(r1,r2+l2-l1,l1) == 0)) return(l2-l1); return(0); } /* * add_to_transited Adds the name of the realm which issued the * ticket granting ticket on which the new ticket to * be issued is based (note that this is the same as * the realm of the server listed in the ticket * granting ticket. * * ASSUMPTIONS: This procedure assumes that the transited field from * the existing ticket granting ticket already appears * in compressed form. It will add the new realm while * maintaining that form. As long as each successive * realm is added using this (or a similar) routine, the * transited field will be in compressed form. The * basis step is an empty transited field which is, by * its nature, in its most compressed form. * * ARGUMENTS: krb5_data *tgt_trans Transited field from TGT * krb5_data *new_trans The transited field for the new ticket * krb5_principal tgs Name of ticket granting server * This includes the realm of the KDC * that issued the ticket granting * ticket. This is the realm that is * to be added to the transited field. * krb5_principal client Name of the client * krb5_principal server The name of the requested server. * This may be the an intermediate * ticket granting server. * * The last two argument are needed since they are * implicitly part of the transited field of the new ticket * even though they are not explicitly listed. * * RETURNS: krb5_error_code - Success, or out of memory * * MODIFIES: new_trans: ->length will contain the length of the new * transited field. * * If ->data was not null when this procedure * is called, the memory referenced by ->data * will be deallocated. * * Memory will be allocated for the new transited field * ->data will be updated to point to the newly * allocated memory. * * BUGS: The space allocated for the new transited field is the * maximum that might be needed given the old transited field, * and the realm to be added. This length is calculated * assuming that no compression of the new realm is possible. * This has no adverse consequences other than the allocation * of more space than required. * * This procedure will not yet use the null subfield notation, * and it will get confused if it sees it. * * This procedure does not check for quoted commas in realm * names. */ static char * data2string (krb5_data *d) { char *s; s = malloc(d->length + 1); if (s) { memcpy(s, d->data, d->length); s[d->length] = 0; } return s; } krb5_error_code add_to_transited(krb5_data *tgt_trans, krb5_data *new_trans, krb5_principal tgs, krb5_principal client, krb5_principal server) { krb5_error_code retval; char *realm; char *trans; char *otrans, *otrans_ptr; /* The following are for stepping through the transited field */ char prev[MAX_REALM_LN]; char next[MAX_REALM_LN]; char current[MAX_REALM_LN]; char exp[MAX_REALM_LN]; /* Expanded current realm name */ int i; int clst, nlst; /* count of last character in current and next */ int pl, pl1; /* prefix length */ int added; /* TRUE = new realm has been added */ realm = data2string(krb5_princ_realm(kdc_context, tgs)); if (realm == NULL) return(ENOMEM); otrans = data2string(tgt_trans); if (otrans == NULL) { free(realm); return(ENOMEM); } /* Keep track of start so we can free */ otrans_ptr = otrans; /* +1 for null, +1 for extra comma which may be added between +1 for potential space when leading slash in realm */ if (!(trans = (char *) malloc(strlen(realm) + strlen(otrans) + 3))) { retval = ENOMEM; goto fail; } if (new_trans->data) free(new_trans->data); new_trans->data = trans; new_trans->length = 0; trans[0] = '\0'; /* For the purpose of appending, the realm preceding the first */ /* realm in the transited field is considered the null realm */ prev[0] = '\0'; /* read field into current */ for (i = 0; *otrans != '\0';) { if (*otrans == '\\') { if (*(++otrans) == '\0') break; else continue; } if (*otrans == ',') { otrans++; break; } current[i++] = *otrans++; if (i >= MAX_REALM_LN) { retval = KRB5KRB_AP_ERR_ILL_CR_TKT; goto fail; } } current[i] = '\0'; added = (krb5_princ_realm(kdc_context, client)->length == strlen(realm) && !strncmp(krb5_princ_realm(kdc_context, client)->data, realm, strlen(realm))) || (krb5_princ_realm(kdc_context, server)->length == strlen(realm) && !strncmp(krb5_princ_realm(kdc_context, server)->data, realm, strlen(realm))); while (current[0]) { /* figure out expanded form of current name */ clst = strlen(current) - 1; if (current[0] == ' ') { strncpy(exp, current+1, sizeof(exp) - 1); exp[sizeof(exp) - 1] = '\0'; } else if ((current[0] == '/') && (prev[0] == '/')) { strncpy(exp, prev, sizeof(exp) - 1); exp[sizeof(exp) - 1] = '\0'; if (strlen(exp) + strlen(current) + 1 >= MAX_REALM_LN) { retval = KRB5KRB_AP_ERR_ILL_CR_TKT; goto fail; } strncat(exp, current, sizeof(exp) - 1 - strlen(exp)); } else if (current[clst] == '.') { strncpy(exp, current, sizeof(exp) - 1); exp[sizeof(exp) - 1] = '\0'; if (strlen(exp) + strlen(prev) + 1 >= MAX_REALM_LN) { retval = KRB5KRB_AP_ERR_ILL_CR_TKT; goto fail; } strncat(exp, prev, sizeof(exp) - 1 - strlen(exp)); } else { strncpy(exp, current, sizeof(exp) - 1); exp[sizeof(exp) - 1] = '\0'; } /* read field into next */ for (i = 0; *otrans != '\0';) { if (*otrans == '\\') { if (*(++otrans) == '\0') break; else continue; } if (*otrans == ',') { otrans++; break; } next[i++] = *otrans++; if (i >= MAX_REALM_LN) { retval = KRB5KRB_AP_ERR_ILL_CR_TKT; goto fail; } } next[i] = '\0'; nlst = i - 1; if (!strcmp(exp, realm)) added = TRUE; /* If we still have to insert the new realm */ if (!added) { /* Is the next field compressed? If not, and if the new */ /* realm is a subrealm of the current realm, compress */ /* the new realm, and insert immediately following the */ /* current one. Note that we can not do this if the next*/ /* field is already compressed since it would mess up */ /* what has already been done. In most cases, this is */ /* not a problem because the realm to be added will be a */ /* subrealm of the next field too, and we will catch */ /* it in a future iteration. */ /* Note that the second test here is an unsigned comparison, so the first half (or a cast) is also required. */ assert(nlst < 0 || nlst < sizeof(next)); if ((nlst < 0 || next[nlst] != '.') && (next[0] != '/') && (pl = subrealm(exp, realm))) { added = TRUE; current[sizeof(current) - 1] = '\0'; if (strlen(current) + (pl>0?pl:-pl) + 2 >= MAX_REALM_LN) { retval = KRB5KRB_AP_ERR_ILL_CR_TKT; goto fail; } strncat(current, ",", sizeof(current) - 1 - strlen(current)); if (pl > 0) { strncat(current, realm, (unsigned) pl); } else { strncat(current, realm+strlen(realm)+pl, (unsigned) (-pl)); } } /* Whether or not the next field is compressed, if the */ /* realm to be added is a superrealm of the current realm,*/ /* then the current realm can be compressed. First the */ /* realm to be added must be compressed relative to the */ /* previous realm (if possible), and then the current */ /* realm compressed relative to the new realm. Note that */ /* if the realm to be added is also a superrealm of the */ /* previous realm, it would have been added earlier, and */ /* we would not reach this step this time around. */ else if ((pl = subrealm(realm, exp))) { added = TRUE; current[0] = '\0'; if ((pl1 = subrealm(prev,realm))) { if (strlen(current) + (pl1>0?pl1:-pl1) + 1 >= MAX_REALM_LN) { retval = KRB5KRB_AP_ERR_ILL_CR_TKT; goto fail; } if (pl1 > 0) { strncat(current, realm, (unsigned) pl1); } else { strncat(current, realm+strlen(realm)+pl1, (unsigned) (-pl1)); } } else { /* If not a subrealm */ if ((realm[0] == '/') && prev[0]) { if (strlen(current) + 2 >= MAX_REALM_LN) { retval = KRB5KRB_AP_ERR_ILL_CR_TKT; goto fail; } strncat(current, " ", sizeof(current) - 1 - strlen(current)); current[sizeof(current) - 1] = '\0'; } if (strlen(current) + strlen(realm) + 1 >= MAX_REALM_LN) { retval = KRB5KRB_AP_ERR_ILL_CR_TKT; goto fail; } strncat(current, realm, sizeof(current) - 1 - strlen(current)); current[sizeof(current) - 1] = '\0'; } if (strlen(current) + (pl>0?pl:-pl) + 2 >= MAX_REALM_LN) { retval = KRB5KRB_AP_ERR_ILL_CR_TKT; goto fail; } strncat(current,",", sizeof(current) - 1 - strlen(current)); current[sizeof(current) - 1] = '\0'; if (pl > 0) { strncat(current, exp, (unsigned) pl); } else { strncat(current, exp+strlen(exp)+pl, (unsigned)(-pl)); } } } if (new_trans->length != 0) { if (strlen(trans) + 2 >= MAX_REALM_LN) { retval = KRB5KRB_AP_ERR_ILL_CR_TKT; goto fail; } strcat(trans, ","); } if (strlen(trans) + strlen(current) + 1 >= MAX_REALM_LN) { retval = KRB5KRB_AP_ERR_ILL_CR_TKT; goto fail; } strcat(trans, current); new_trans->length = strlen(trans); strncpy(prev, exp, sizeof(prev) - 1); prev[sizeof(prev) - 1] = '\0'; strncpy(current, next, sizeof(current) - 1); current[sizeof(current) - 1] = '\0'; } if (!added) { if (new_trans->length != 0) { if (strlen(trans) + 2 >= MAX_REALM_LN) { retval = KRB5KRB_AP_ERR_ILL_CR_TKT; goto fail; } strcat(trans, ","); } if((realm[0] == '/') && trans[0]) { if (strlen(trans) + 2 >= MAX_REALM_LN) { retval = KRB5KRB_AP_ERR_ILL_CR_TKT; goto fail; } strcat(trans, " "); } if (strlen(trans) + strlen(realm) + 1 >= MAX_REALM_LN) { retval = KRB5KRB_AP_ERR_ILL_CR_TKT; goto fail; } strcat(trans, realm); new_trans->length = strlen(trans); } retval = 0; fail: free(realm); free(otrans_ptr); return (retval); } /* * Routines that validate a AS request; checks a lot of things. :-) * * Returns a Kerberos protocol error number, which is _not_ the same * as a com_err error number! */ #define AS_INVALID_OPTIONS (KDC_OPT_FORWARDED | KDC_OPT_PROXY |\ KDC_OPT_VALIDATE | KDC_OPT_RENEW | KDC_OPT_ENC_TKT_IN_SKEY) int validate_as_request(register krb5_kdc_req *request, krb5_db_entry client, krb5_db_entry server, krb5_timestamp kdc_time, const char **status) { int errcode; /* * If an option is set that is only allowed in TGS requests, complain. */ if (request->kdc_options & AS_INVALID_OPTIONS) { *status = "INVALID AS OPTIONS"; return KDC_ERR_BADOPTION; } /* The client's password must not be expired, unless the server is a KRB5_KDC_PWCHANGE_SERVICE. */ if (client.pw_expiration && client.pw_expiration < kdc_time && !isflagset(server.attributes, KRB5_KDB_PWCHANGE_SERVICE)) { *status = "CLIENT KEY EXPIRED"; #ifdef KRBCONF_VAGUE_ERRORS return(KRB_ERR_GENERIC); #else return(KDC_ERR_KEY_EXP); #endif } /* The client must not be expired */ if (client.expiration && client.expiration < kdc_time) { *status = "CLIENT EXPIRED"; #ifdef KRBCONF_VAGUE_ERRORS return(KRB_ERR_GENERIC); #else return(KDC_ERR_NAME_EXP); #endif } /* The server must not be expired */ if (server.expiration && server.expiration < kdc_time) { *status = "SERVICE EXPIRED"; return(KDC_ERR_SERVICE_EXP); } /* * If the client requires password changing, then only allow the * pwchange service. */ if (isflagset(client.attributes, KRB5_KDB_REQUIRES_PWCHANGE) && !isflagset(server.attributes, KRB5_KDB_PWCHANGE_SERVICE)) { *status = "REQUIRED PWCHANGE"; return(KDC_ERR_KEY_EXP); } /* Client and server must allow postdating tickets */ if ((isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE) || isflagset(request->kdc_options, KDC_OPT_POSTDATED)) && (isflagset(client.attributes, KRB5_KDB_DISALLOW_POSTDATED) || isflagset(server.attributes, KRB5_KDB_DISALLOW_POSTDATED))) { *status = "POSTDATE NOT ALLOWED"; return(KDC_ERR_CANNOT_POSTDATE); } /* Client and server must allow forwardable tickets */ if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE) && (isflagset(client.attributes, KRB5_KDB_DISALLOW_FORWARDABLE) || isflagset(server.attributes, KRB5_KDB_DISALLOW_FORWARDABLE))) { *status = "FORWARDABLE NOT ALLOWED"; return(KDC_ERR_POLICY); } /* Client and server must allow renewable tickets */ if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE) && (isflagset(client.attributes, KRB5_KDB_DISALLOW_RENEWABLE) || isflagset(server.attributes, KRB5_KDB_DISALLOW_RENEWABLE))) { *status = "RENEWABLE NOT ALLOWED"; return(KDC_ERR_POLICY); } /* Client and server must allow proxiable tickets */ if (isflagset(request->kdc_options, KDC_OPT_PROXIABLE) && (isflagset(client.attributes, KRB5_KDB_DISALLOW_PROXIABLE) || isflagset(server.attributes, KRB5_KDB_DISALLOW_PROXIABLE))) { *status = "PROXIABLE NOT ALLOWED"; return(KDC_ERR_POLICY); } /* Check to see if client is locked out */ if (isflagset(client.attributes, KRB5_KDB_DISALLOW_ALL_TIX)) { *status = "CLIENT LOCKED OUT"; return(KDC_ERR_C_PRINCIPAL_UNKNOWN); } /* Check to see if server is locked out */ if (isflagset(server.attributes, KRB5_KDB_DISALLOW_ALL_TIX)) { *status = "SERVICE LOCKED OUT"; return(KDC_ERR_S_PRINCIPAL_UNKNOWN); } /* Check to see if server is allowed to be a service */ if (isflagset(server.attributes, KRB5_KDB_DISALLOW_SVR)) { *status = "SERVICE NOT ALLOWED"; return(KDC_ERR_S_PRINCIPAL_UNKNOWN); } /* * Check against local policy */ errcode = against_local_policy_as(request, server, client, kdc_time, status); if (errcode) return errcode; return 0; } #define ASN1_ID_CLASS (0xc0) #define ASN1_ID_TYPE (0x20) #define ASN1_ID_TAG (0x1f) #define ASN1_CLASS_UNIV (0) #define ASN1_CLASS_APP (1) #define ASN1_CLASS_CTX (2) #define ASN1_CLASS_PRIV (3) #define asn1_id_constructed(x) (x & ASN1_ID_TYPE) #define asn1_id_primitive(x) (!asn1_id_constructed(x)) #define asn1_id_class(x) ((x & ASN1_ID_CLASS) >> 6) #define asn1_id_tag(x) (x & ASN1_ID_TAG) /* * asn1length - return encoded length of value. * * passed a pointer into the asn.1 stream, which is updated * to point right after the length bits. * * returns -1 on failure. */ static int asn1length(unsigned char **astream) { int length; /* resulting length */ int sublen; /* sublengths */ int blen; /* bytes of length */ unsigned char *p; /* substring searching */ if (**astream & 0x80) { blen = **astream & 0x7f; if (blen > 3) { return(-1); } for (++*astream, length = 0; blen; ++*astream, blen--) { length = (length << 8) | **astream; } if (length == 0) { /* indefinite length, figure out by hand */ p = *astream; p++; while (1) { /* compute value length. */ if ((sublen = asn1length(&p)) < 0) { return(-1); } p += sublen; /* check for termination */ if ((!*p++) && (!*p)) { p++; break; } } length = p - *astream; } } else { length = **astream; ++*astream; } return(length); } /* * fetch_asn1_field - return raw asn.1 stream of subfield. * * this routine is passed a context-dependent tag number and "level" and returns * the size and length of the corresponding level subfield. * * levels and are numbered starting from 1. * * returns 0 on success, -1 otherwise. */ int fetch_asn1_field(unsigned char *astream, unsigned int level, unsigned int field, krb5_data *data) { unsigned char *estream; /* end of stream */ int classes; /* # classes seen so far this level */ unsigned int levels = 0; /* levels seen so far */ int lastlevel = 1000; /* last level seen */ int length; /* various lengths */ int tag; /* tag number */ unsigned char savelen; /* saved length of our field */ classes = -1; /* we assume that the first identifier/length will tell us how long the entire stream is. */ astream++; estream = astream; if ((length = asn1length(&astream)) < 0) { return(-1); } estream += length; /* search down the stream, checking identifiers. we process identifiers until we hit the "level" we want, and then process that level for our subfield, always making sure we don't go off the end of the stream. */ while (astream < estream) { if (!asn1_id_constructed(*astream)) { return(-1); } if (asn1_id_class(*astream) == ASN1_CLASS_CTX) { if ((tag = (int)asn1_id_tag(*astream)) <= lastlevel) { levels++; classes = -1; } lastlevel = tag; if (levels == level) { /* in our context-dependent class, is this the one we're looking for ? */ if (tag == field) { /* return length and data */ astream++; savelen = *astream; if ((data->length = asn1length(&astream)) < 0) { return(-1); } /* if the field length is indefinite, we will have to subtract two (terminating octets) from the length returned since we don't want to pass any info from the "wrapper" back. asn1length will always return the *total* length of the field, not just what's contained in it */ if ((savelen & 0xff) == 0x80) { data->length -=2 ; } data->data = (char *)astream; return(0); } else if (tag <= classes) { /* we've seen this class before, something must be wrong */ return(-1); } else { classes = tag; } } } /* if we're not on our level yet, process this value. otherwise skip over it */ astream++; if ((length = asn1length(&astream)) < 0) { return(-1); } if (levels == level) { astream += length; } } return(-1); } /* * Routines that validate a TGS request; checks a lot of things. :-) * * Returns a Kerberos protocol error number, which is _not_ the same * as a com_err error number! */ #define TGS_OPTIONS_HANDLED (KDC_OPT_FORWARDABLE | KDC_OPT_FORWARDED | \ KDC_OPT_PROXIABLE | KDC_OPT_PROXY | \ KDC_OPT_ALLOW_POSTDATE | KDC_OPT_POSTDATED | \ KDC_OPT_RENEWABLE | KDC_OPT_RENEWABLE_OK | \ KDC_OPT_ENC_TKT_IN_SKEY | KDC_OPT_RENEW | \ KDC_OPT_VALIDATE) #define NO_TGT_OPTION (KDC_OPT_FORWARDED | KDC_OPT_PROXY | KDC_OPT_RENEW | \ KDC_OPT_VALIDATE) int validate_tgs_request(register krb5_kdc_req *request, krb5_db_entry server, krb5_ticket *ticket, krb5_timestamp kdc_time, const char **status) { int errcode; int st_idx = 0; /* * If an illegal option is set, ignore it. */ request->kdc_options &= TGS_OPTIONS_HANDLED; /* Check to see if server has expired */ if (server.expiration && server.expiration < kdc_time) { *status = "SERVICE EXPIRED"; return(KDC_ERR_SERVICE_EXP); } /* * Verify that the server principal in authdat->ticket is correct * (either the ticket granting service or the service that was * originally requested) */ if (request->kdc_options & NO_TGT_OPTION) { if (!krb5_principal_compare(kdc_context, ticket->server, request->server)) { *status = "SERVER DIDN'T MATCH TICKET FOR RENEW/FORWARD/ETC"; return(KDC_ERR_SERVER_NOMATCH); } } else { /* * OK, we need to validate the krbtgt service in the ticket. * * The krbtgt service is of the form: * krbtgt/realm-A@realm-B * * Realm A is the "server realm"; the realm of the * server of the requested ticket must match this realm. * Of course, it should be a realm serviced by this KDC. * * Realm B is the "client realm"; this is what should be * added to the transited field. (which is done elsewhere) */ /* Make sure there are two components... */ if (krb5_princ_size(kdc_context, ticket->server) != 2) { *status = "BAD TGS SERVER LENGTH"; return KRB_AP_ERR_NOT_US; } /* ...that the first component is krbtgt... */ if (!krb5_is_tgs_principal(ticket->server)) { *status = "BAD TGS SERVER NAME"; return KRB_AP_ERR_NOT_US; } /* ...and that the second component matches the server realm... */ if ((krb5_princ_size(kdc_context, ticket->server) <= 1) || (krb5_princ_component(kdc_context, ticket->server, 1)->length != krb5_princ_realm(kdc_context, request->server)->length) || memcmp(krb5_princ_component(kdc_context, ticket->server, 1)->data, krb5_princ_realm(kdc_context, request->server)->data, krb5_princ_realm(kdc_context, request->server)->length)) { *status = "BAD TGS SERVER INSTANCE"; return KRB_AP_ERR_NOT_US; } /* XXX add check that second component must match locally * supported realm? */ /* Server must allow TGS based issuances */ if (isflagset(server.attributes, KRB5_KDB_DISALLOW_TGT_BASED)) { *status = "TGT BASED NOT ALLOWED"; return(KDC_ERR_POLICY); } } /* TGS must be forwardable to get forwarded or forwardable ticket */ if ((isflagset(request->kdc_options, KDC_OPT_FORWARDED) || isflagset(request->kdc_options, KDC_OPT_FORWARDABLE)) && !isflagset(ticket->enc_part2->flags, TKT_FLG_FORWARDABLE)) { *status = "TGT NOT FORWARDABLE"; return KDC_ERR_BADOPTION; } /* TGS must be proxiable to get proxiable ticket */ if ((isflagset(request->kdc_options, KDC_OPT_PROXY) || isflagset(request->kdc_options, KDC_OPT_PROXIABLE)) && !isflagset(ticket->enc_part2->flags, TKT_FLG_PROXIABLE)) { *status = "TGT NOT PROXIABLE"; return KDC_ERR_BADOPTION; } /* TGS must allow postdating to get postdated ticket */ if ((isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE) || isflagset(request->kdc_options, KDC_OPT_POSTDATED)) && !isflagset(ticket->enc_part2->flags, TKT_FLG_MAY_POSTDATE)) { *status = "TGT NOT POSTDATABLE"; return KDC_ERR_BADOPTION; } /* can only validate invalid tix */ if (isflagset(request->kdc_options, KDC_OPT_VALIDATE) && !isflagset(ticket->enc_part2->flags, TKT_FLG_INVALID)) { *status = "VALIDATE VALID TICKET"; return KDC_ERR_BADOPTION; } /* can only renew renewable tix */ if ((isflagset(request->kdc_options, KDC_OPT_RENEW) || isflagset(request->kdc_options, KDC_OPT_RENEWABLE)) && !isflagset(ticket->enc_part2->flags, TKT_FLG_RENEWABLE)) { *status = "TICKET NOT RENEWABLE"; return KDC_ERR_BADOPTION; } /* can not proxy ticket granting tickets */ if (isflagset(request->kdc_options, KDC_OPT_PROXY) && (!request->server->data || request->server->data[0].length != KRB5_TGS_NAME_SIZE || memcmp(request->server->data[0].data, KRB5_TGS_NAME, KRB5_TGS_NAME_SIZE))) { *status = "CAN'T PROXY TGT"; return KDC_ERR_BADOPTION; } /* Server must allow forwardable tickets */ if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE) && isflagset(server.attributes, KRB5_KDB_DISALLOW_FORWARDABLE)) { *status = "NON-FORWARDABLE TICKET"; return(KDC_ERR_POLICY); } /* Server must allow renewable tickets */ if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE) && isflagset(server.attributes, KRB5_KDB_DISALLOW_RENEWABLE)) { *status = "NON-RENEWABLE TICKET"; return(KDC_ERR_POLICY); } /* Server must allow proxiable tickets */ if (isflagset(request->kdc_options, KDC_OPT_PROXIABLE) && isflagset(server.attributes, KRB5_KDB_DISALLOW_PROXIABLE)) { *status = "NON-PROXIABLE TICKET"; return(KDC_ERR_POLICY); } /* Server must allow postdated tickets */ if (isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE) && isflagset(server.attributes, KRB5_KDB_DISALLOW_POSTDATED)) { *status = "NON-POSTDATABLE TICKET"; return(KDC_ERR_CANNOT_POSTDATE); } /* Server must allow DUP SKEY requests */ if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY) && isflagset(server.attributes, KRB5_KDB_DISALLOW_DUP_SKEY)) { *status = "DUP_SKEY DISALLOWED"; return(KDC_ERR_POLICY); } /* Server must not be locked out */ if (isflagset(server.attributes, KRB5_KDB_DISALLOW_ALL_TIX)) { *status = "SERVER LOCKED OUT"; return(KDC_ERR_S_PRINCIPAL_UNKNOWN); } /* Server must be allowed to be a service */ if (isflagset(server.attributes, KRB5_KDB_DISALLOW_SVR)) { *status = "SERVER NOT ALLOWED"; return(KDC_ERR_S_PRINCIPAL_UNKNOWN); } /* Check the hot list */ if (check_hot_list(ticket)) { *status = "HOT_LIST"; return(KRB_AP_ERR_REPEAT); } /* Check the start time vs. the KDC time */ if (isflagset(request->kdc_options, KDC_OPT_VALIDATE)) { if (ticket->enc_part2->times.starttime > kdc_time) { *status = "NOT_YET_VALID"; return(KRB_AP_ERR_TKT_NYV); } } /* * Check the renew_till time. The endtime was already * been checked in the initial authentication check. */ if (isflagset(request->kdc_options, KDC_OPT_RENEW) && (ticket->enc_part2->times.renew_till < kdc_time)) { *status = "TKT_EXPIRED"; return(KRB_AP_ERR_TKT_EXPIRED); } /* * Checks for ENC_TKT_IN_SKEY: * * (1) Make sure the second ticket exists * (2) Make sure it is a ticket granting ticket */ if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY)) { if (!request->second_ticket || !request->second_ticket[st_idx]) { *status = "NO_2ND_TKT"; return(KDC_ERR_BADOPTION); } if (!krb5_principal_compare(kdc_context, request->second_ticket[st_idx]->server, tgs_server)) { *status = "2ND_TKT_NOT_TGS"; return(KDC_ERR_POLICY); } st_idx++; } /* Check for hardware preauthentication */ if (isflagset(server.attributes, KRB5_KDB_REQUIRES_HW_AUTH) && !isflagset(ticket->enc_part2->flags,TKT_FLG_HW_AUTH)) { *status = "NO HW PREAUTH"; return KRB_ERR_GENERIC; } /* Check for any kind of preauthentication */ if (isflagset(server.attributes, KRB5_KDB_REQUIRES_PRE_AUTH) && !isflagset(ticket->enc_part2->flags, TKT_FLG_PRE_AUTH)) { *status = "NO PREAUTH"; return KRB_ERR_GENERIC; } /* * Check local policy */ errcode = against_local_policy_tgs(request, server, ticket, status); if (errcode) return errcode; return 0; } /* * This function returns 1 if the dbentry has a key for a specified * keytype, and 0 if not. */ int dbentry_has_key_for_enctype(krb5_context context, krb5_db_entry *client, krb5_enctype enctype) { krb5_error_code retval; krb5_key_data *datap; retval = krb5_dbe_find_enctype(context, client, enctype, -1, 0, &datap); if (retval) return 0; else return 1; } /* * This function returns 1 if the entity referenced by this * structure can support the a particular encryption system, and 0 if * not. * * XXX eventually this information should be looked up in the * database. Since it isn't, we use some hueristics and attribute * options bits for now. */ int dbentry_supports_enctype(krb5_context context, krb5_db_entry *client, krb5_enctype enctype) { /* * If it's DES_CBC_MD5, there's a bit in the attribute mask which * checks to see if we support it. * * In theory everything's supposed to support DES_CBC_MD5, but * that's not the reality.... */ /* * We are assuming that all entries can support MD5; this information * need not be kept in the database. */ if (enctype == ENCTYPE_DES_CBC_MD5) return 1; /* * XXX we assume everything can understand DES_CBC_CRC */ if (enctype == ENCTYPE_DES_CBC_CRC) return 1; /* * If we have a key for the encryption system, we assume it's * supported. */ return dbentry_has_key_for_enctype(context, client, enctype); } /* * This function returns the keytype which should be selected for the * session key. It is based on the ordered list which the user * requested, and what the KDC and the application server can support. */ krb5_enctype select_session_keytype(krb5_context context, krb5_db_entry *server, int nktypes, krb5_enctype *ktype) { int i; for (i = 0; i < nktypes; i++) { if (!krb5_c_valid_enctype(ktype[i])) continue; if (!krb5_is_permitted_enctype(context, ktype[i])) continue; if (dbentry_supports_enctype(context, server, ktype[i])) return ktype[i]; } return 0; } /* * This function returns salt information for a particular client_key */ krb5_error_code get_salt_from_key(krb5_context context, krb5_principal client, krb5_key_data *client_key, krb5_data *salt) { krb5_error_code retval; krb5_data * realm; salt->data = 0; salt->length = SALT_TYPE_NO_LENGTH; if (client_key->key_data_ver == 1) return 0; switch (client_key->key_data_type[1]) { case KRB5_KDB_SALTTYPE_NORMAL: break; case KRB5_KDB_SALTTYPE_V4: /* send an empty (V4) salt */ salt->data = 0; salt->length = 0; break; case KRB5_KDB_SALTTYPE_NOREALM: if ((retval = krb5_principal2salt_norealm(context, client, salt))) return retval; break; case KRB5_KDB_SALTTYPE_AFS3: /* send the same salt as with onlyrealm - but with no type info, we just hope they figure it out on the other end. */ /* fall through to onlyrealm: */ case KRB5_KDB_SALTTYPE_ONLYREALM: realm = krb5_princ_realm(context, client); salt->length = realm->length; if ((salt->data = malloc(realm->length)) == NULL) return ENOMEM; memcpy(salt->data, realm->data, realm->length); break; case KRB5_KDB_SALTTYPE_SPECIAL: salt->length = client_key->key_data_length[1]; if ((salt->data = malloc(salt->length)) == NULL) return ENOMEM; memcpy(salt->data, client_key->key_data_contents[1], salt->length); break; } return 0; } /* * Limit strings to a "reasonable" length to prevent crowding out of * other useful information in the log entry */ #define NAME_LENGTH_LIMIT 128 void limit_string(char *name) { int i; if (!name) return; if (strlen(name) < NAME_LENGTH_LIMIT) return; i = NAME_LENGTH_LIMIT-4; name[i++] = '.'; name[i++] = '.'; name[i++] = '.'; name[i] = '\0'; return; } /* * L10_2 = log10(2**x), rounded up; log10(2) ~= 0.301. */ #define L10_2(x) ((int)(((x * 301) + 999) / 1000)) /* * Max length of sprintf("%ld") for an int of type T; includes leading * minus sign and terminating NUL. */ #define D_LEN(t) (L10_2(sizeof(t) * CHAR_BIT) + 2) void ktypes2str(char *s, size_t len, int nktypes, krb5_enctype *ktype) { int i; char stmp[D_LEN(krb5_enctype) + 1]; char *p; if (nktypes < 0 || len < (sizeof(" etypes {...}") + D_LEN(int))) { *s = '\0'; return; } sprintf(s, "%d etypes {", nktypes); for (i = 0; i < nktypes; i++) { sprintf(stmp, "%s%ld", i ? " " : "", (long)ktype[i]); if (strlen(s) + strlen(stmp) + sizeof("}") > len) break; strcat(s, stmp); } if (i < nktypes) { /* * We broke out of the loop. Try to truncate the list. */ p = s + strlen(s); while (p - s + sizeof("...}") > len) { while (p > s && *p != ' ' && *p != '{') *p-- = '\0'; if (p > s && *p == ' ') { *p-- = '\0'; continue; } } strcat(s, "..."); } strcat(s, "}"); return; } void rep_etypes2str(char *s, size_t len, krb5_kdc_rep *rep) { char stmp[sizeof("ses=") + D_LEN(krb5_enctype)]; if (len < (3 * D_LEN(krb5_enctype) + sizeof("etypes {rep= tkt= ses=}"))) { *s = '\0'; return; } sprintf(s, "etypes {rep=%ld", (long)rep->enc_part.enctype); if (rep->ticket != NULL) { sprintf(stmp, " tkt=%ld", (long)rep->ticket->enc_part.enctype); strcat(s, stmp); } if (rep->ticket != NULL && rep->ticket->enc_part2 != NULL && rep->ticket->enc_part2->session != NULL) { sprintf(stmp, " ses=%ld", (long)rep->ticket->enc_part2->session->enctype); strcat(s, stmp); } strcat(s, "}"); return; } /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * kdc/kdc_util.h * * Copyright 1990 by the Massachusetts Institute of Technology. * * 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. * * * Declarations for policy.c */ #ifndef __KRB5_KDC_UTIL__ #define __KRB5_KDC_UTIL__ #include "kdb.h" #ifdef __cplusplus extern "C" { #endif typedef struct _krb5_fulladdr { krb5_address * address; krb5_ui_4 port; } krb5_fulladdr; krb5_error_code check_hot_list (krb5_ticket *); krb5_boolean realm_compare (krb5_principal, krb5_principal); krb5_boolean krb5_is_tgs_principal (krb5_principal); krb5_error_code add_to_transited (krb5_data *, krb5_data *, krb5_principal, krb5_principal, krb5_principal); krb5_error_code compress_transited (krb5_data *, krb5_principal, krb5_data *); krb5_error_code concat_authorization_data (krb5_authdata **, krb5_authdata **, krb5_authdata ***); krb5_error_code fetch_last_req_info (krb5_db_entry *, krb5_last_req_entry ***); krb5_error_code kdc_convert_key (krb5_keyblock *, krb5_keyblock *, int); krb5_error_code kdc_process_tgs_req (krb5_kdc_req *, const krb5_fulladdr *, krb5_data *, krb5_ticket **, krb5_keyblock **); krb5_error_code kdc_get_server_key (krb5_ticket *, krb5_keyblock **, krb5_kvno *); int validate_as_request (krb5_kdc_req *, krb5_db_entry, krb5_db_entry, krb5_timestamp, const char **); int validate_tgs_request (krb5_kdc_req *, krb5_db_entry, krb5_ticket *, krb5_timestamp, const char **); int fetch_asn1_field (unsigned char *, unsigned int, unsigned int, krb5_data *); int dbentry_has_key_for_enctype (krb5_context context, krb5_db_entry *client, krb5_enctype enctype); int dbentry_supports_enctype (krb5_context context, krb5_db_entry *client, krb5_enctype enctype); krb5_enctype select_session_keytype (krb5_context context, krb5_db_entry *server, int nktypes, krb5_enctype *ktypes); krb5_error_code get_salt_from_key (krb5_context, krb5_principal, krb5_key_data *, krb5_data *); void limit_string (char *name); void ktypes2str(char *s, size_t len, int nktypes, krb5_enctype *ktype); void rep_etypes2str(char *s, size_t len, krb5_kdc_rep *rep); /* do_as_req.c */ krb5_error_code process_as_req (krb5_kdc_req *, krb5_data *, const krb5_fulladdr *, krb5_data ** ); /* do_tgs_req.c */ krb5_error_code process_tgs_req (krb5_data *, const krb5_fulladdr *, krb5_data ** ); /* dispatch.c */ krb5_error_code dispatch (krb5_data *, const krb5_fulladdr *, krb5_data **); /* main.c */ krb5_error_code kdc_initialize_rcache (krb5_context, char *); krb5_error_code setup_server_realm (krb5_principal); /* network.c */ krb5_error_code listen_and_process (const char *); krb5_error_code setup_network (const char *); krb5_error_code closedown_network (const char *); /* policy.c */ int against_local_policy_as (krb5_kdc_req *, krb5_db_entry, krb5_db_entry, krb5_timestamp, const char **); int against_local_policy_tgs (krb5_kdc_req *, krb5_db_entry, krb5_ticket *, const char **); /* kdc_preauth.c */ const char * missing_required_preauth (krb5_db_entry *client, krb5_db_entry *server, krb5_enc_tkt_part *enc_tkt_reply); void get_preauth_hint_list (krb5_kdc_req * request, krb5_db_entry *client, krb5_db_entry *server, krb5_data *e_data); krb5_error_code load_preauth_plugins(krb5_context context); krb5_error_code unload_preauth_plugins(krb5_context context); krb5_error_code check_padata (krb5_context context, krb5_db_entry *client, krb5_data *req_pkt, krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply, void **padata_context, krb5_data *e_data); krb5_error_code return_padata (krb5_context context, krb5_db_entry *client, krb5_data *req_pkt, krb5_kdc_req *request, krb5_kdc_rep *reply, krb5_key_data *client_key, krb5_keyblock *encrypting_key, void **padata_context); krb5_error_code free_padata_context (krb5_context context, void **padata_context); /* replay.c */ krb5_boolean kdc_check_lookaside (krb5_data *, krb5_data **); void kdc_insert_lookaside (krb5_data *, krb5_data *); void kdc_free_lookaside(krb5_context); /* which way to convert key? */ #define CONVERT_INTO_DB 0 #define CONVERT_OUTOF_DB 1 #define isflagset(flagfield, flag) (flagfield & (flag)) #define setflag(flagfield, flag) (flagfield |= (flag)) #define clear(flagfield, flag) (flagfield &= ~(flag)) #ifdef KRB5_KRB4_COMPAT krb5_error_code process_v4 (const krb5_data *, const krb5_fulladdr *, krb5_data **); void process_v4_mode (const char *, const char *); void enable_v4_crossrealm(char *); #else #define process_v4(foo,bar,quux,foobar) KRB5KRB_AP_ERR_BADVERSION #endif #ifndef min #define min(a, b) ((a) < (b) ? (a) : (b)) #define max(a, b) ((a) > (b) ? (a) : (b)) #endif #ifdef KRB5_USE_INET6 #define ADDRTYPE2FAMILY(X) \ ((X) == ADDRTYPE_INET6 ? AF_INET6 : (X) == ADDRTYPE_INET ? AF_INET : -1) #else #define ADDRTYPE2FAMILY(X) \ ((X) == ADDRTYPE_INET ? AF_INET : -1) #endif /* RFC 4120: KRB5KDC_ERR_KEY_TOO_WEAK * RFC 4556: KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED */ #define KRB5KDC_ERR_KEY_TOO_WEAK KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED #ifdef __cplusplus } #endif #endif /* !__KRB5_KDC_UTIL__ */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * kdc/main.c * * Copyright 1990,2001 by the Massachusetts Institute of Technology. * * 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. * * * Main procedure body for the KDC server process. */ #include #include #include #include #include #include "k5-int.h" #include "com_err.h" #include "adm.h" #include "adm_proto.h" #include "kdc_util.h" #include "extern.h" #include "kdc5_err.h" #include #include #ifdef HAVE_NETINET_IN_H #include #endif #ifdef KRB5_KRB4_COMPAT #include #endif #if defined(NEED_DAEMON_PROTO) extern int daemon(int, int); #endif void usage (char *); krb5_sigtype request_exit (int); krb5_sigtype request_hup (int); void setup_signal_handlers (void); krb5_error_code setup_sam (void); void initialize_realms (krb5_context, int, char **); void finish_realms (char *); static int nofork = 0; static int rkey_init_done = 0; /* Solaris Kerberos: global here that other functions access */ int max_tcp_data_connections; #ifdef POSIX_SIGNALS static struct sigaction s_action; #endif /* POSIX_SIGNALS */ #define KRB5_KDC_MAX_REALMS 32 /* * Find the realm entry for a given realm. */ kdc_realm_t * find_realm_data(char *rname, krb5_ui_4 rsize) { int i; for (i=0; irealm_name)) && !strncmp(rname, kdc_realmlist[i]->realm_name, rsize)) return(kdc_realmlist[i]); } return((kdc_realm_t *) NULL); } krb5_error_code setup_server_realm(krb5_principal sprinc) { krb5_error_code kret; kdc_realm_t *newrealm; kret = 0; if (kdc_numrealms > 1) { if (!(newrealm = find_realm_data(sprinc->realm.data, (krb5_ui_4) sprinc->realm.length))) kret = ENOENT; else kdc_active_realm = newrealm; } else kdc_active_realm = kdc_realmlist[0]; return(kret); } static void finish_realm(kdc_realm_t *rdp) { if (rdp->realm_dbname) free(rdp->realm_dbname); if (rdp->realm_mpname) free(rdp->realm_mpname); if (rdp->realm_stash) free(rdp->realm_stash); if (rdp->realm_ports) free(rdp->realm_ports); if (rdp->realm_tcp_ports) free(rdp->realm_tcp_ports); if (rdp->realm_keytab) krb5_kt_close(rdp->realm_context, rdp->realm_keytab); if (rdp->realm_context) { if (rdp->realm_mprinc) krb5_free_principal(rdp->realm_context, rdp->realm_mprinc); if (rdp->realm_mkey.length && rdp->realm_mkey.contents) { memset(rdp->realm_mkey.contents, 0, rdp->realm_mkey.length); free(rdp->realm_mkey.contents); } krb5_db_fini(rdp->realm_context); if (rdp->realm_tgsprinc) krb5_free_principal(rdp->realm_context, rdp->realm_tgsprinc); krb5_free_context(rdp->realm_context); } memset((char *) rdp, 0, sizeof(*rdp)); free(rdp); } /* * Initialize a realm control structure from the alternate profile or from * the specified defaults. * * After we're complete here, the essence of the realm is embodied in the * realm data and we should be all set to begin operation for that realm. */ static krb5_error_code init_realm(krb5_context kcontext, char *progname, kdc_realm_t *rdp, char *realm, char *def_mpname, krb5_enctype def_enctype, char *def_udp_ports, char *def_tcp_ports, krb5_boolean def_manual, char **db_args) { krb5_error_code kret; krb5_boolean manual; krb5_realm_params *rparams; memset((char *) rdp, 0, sizeof(kdc_realm_t)); if (!realm) { kret = EINVAL; goto whoops; } rdp->realm_name = realm; kret = krb5int_init_context_kdc(&rdp->realm_context); if (kret) { com_err(progname, kret, gettext("while getting context for realm %s"), realm); goto whoops; } /* * Solaris Kerberos: * Set the current context to that of the realm being init'ed */ krb5_klog_set_context(rdp->realm_context); kret = krb5_read_realm_params(rdp->realm_context, rdp->realm_name, &rparams); if (kret) { com_err(progname, kret, gettext("while reading realm parameters")); goto whoops; } /* Handle profile file name */ if (rparams && rparams->realm_profile) rdp->realm_profile = strdup(rparams->realm_profile); /* Handle master key name */ if (rparams && rparams->realm_mkey_name) rdp->realm_mpname = strdup(rparams->realm_mkey_name); else rdp->realm_mpname = (def_mpname) ? strdup(def_mpname) : strdup(KRB5_KDB_M_NAME); /* Handle KDC ports */ if (rparams && rparams->realm_kdc_ports) rdp->realm_ports = strdup(rparams->realm_kdc_ports); else rdp->realm_ports = strdup(def_udp_ports); if (rparams && rparams->realm_kdc_tcp_ports) rdp->realm_tcp_ports = strdup(rparams->realm_kdc_tcp_ports); else rdp->realm_tcp_ports = strdup(def_tcp_ports); /* Handle stash file */ if (rparams && rparams->realm_stash_file) { rdp->realm_stash = strdup(rparams->realm_stash_file); manual = FALSE; } else manual = def_manual; /* Handle master key type */ if (rparams && rparams->realm_enctype_valid) rdp->realm_mkey.enctype = (krb5_enctype) rparams->realm_enctype; else rdp->realm_mkey.enctype = manual ? def_enctype : ENCTYPE_UNKNOWN; /* Handle reject-bad-transit flag */ if (rparams && rparams->realm_reject_bad_transit_valid) rdp->realm_reject_bad_transit = rparams->realm_reject_bad_transit; else rdp->realm_reject_bad_transit = 1; /* Handle ticket maximum life */ rdp->realm_maxlife = (rparams && rparams->realm_max_life_valid) ? rparams->realm_max_life : KRB5_KDB_MAX_LIFE; /* Handle ticket renewable maximum life */ rdp->realm_maxrlife = (rparams && rparams->realm_max_rlife_valid) ? rparams->realm_max_rlife : KRB5_KDB_MAX_RLIFE; if (rparams) krb5_free_realm_params(rdp->realm_context, rparams); /* * We've got our parameters, now go and setup our realm context. */ /* Set the default realm of this context */ if ((kret = krb5_set_default_realm(rdp->realm_context, realm))) { com_err(progname, kret, gettext("while setting default realm to %s"), realm); goto whoops; } /* first open the database before doing anything */ #ifdef KRBCONF_KDC_MODIFIES_KDB if ((kret = krb5_db_open(rdp->realm_context, db_args, KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_KDC))) { #else if ((kret = krb5_db_open(rdp->realm_context, db_args, KRB5_KDB_OPEN_RO | KRB5_KDB_SRV_TYPE_KDC))) { #endif /* * Solaris Kerberos: * Make sure that error messages are printed using gettext */ com_err(progname, kret, gettext("while initializing database for realm %s"), realm); goto whoops; } /* Assemble and parse the master key name */ if ((kret = krb5_db_setup_mkey_name(rdp->realm_context, rdp->realm_mpname, rdp->realm_name, (char **) NULL, &rdp->realm_mprinc))) { com_err(progname, kret, gettext("while setting up master key name %s for realm %s"), rdp->realm_mpname, realm); goto whoops; } /* * Get the master key. */ if ((kret = krb5_db_fetch_mkey(rdp->realm_context, rdp->realm_mprinc, rdp->realm_mkey.enctype, manual, FALSE, rdp->realm_stash, 0, &rdp->realm_mkey))) { com_err(progname, kret, gettext("while fetching master key %s for realm %s"), rdp->realm_mpname, realm); goto whoops; } /* Verify the master key */ if ((kret = krb5_db_verify_master_key(rdp->realm_context, rdp->realm_mprinc, &rdp->realm_mkey))) { com_err(progname, kret, gettext("while verifying master key for realm %s"), realm); goto whoops; } if ((kret = krb5_db_set_mkey(rdp->realm_context, &rdp->realm_mkey))) { com_err(progname, kret, gettext("while processing master key for realm %s"), realm); goto whoops; } /* Set up the keytab */ if ((kret = krb5_ktkdb_resolve(rdp->realm_context, NULL, &rdp->realm_keytab))) { com_err(progname, kret, gettext("while resolving kdb keytab for realm %s"), realm); goto whoops; } /* Preformat the TGS name */ if ((kret = krb5_build_principal(rdp->realm_context, &rdp->realm_tgsprinc, strlen(realm), realm, KRB5_TGS_NAME, realm, (char *) NULL))) { com_err(progname, kret, gettext("while building TGS name for realm %s"), realm); goto whoops; } if (!rkey_init_done) { #ifdef KRB5_KRB4_COMPAT krb5_keyblock temp_key; #endif /* * If all that worked, then initialize the random key * generators. */ #ifdef KRB5_KRB4_COMPAT if ((kret = krb5_c_make_random_key(rdp->realm_context, ENCTYPE_DES_CBC_CRC, &temp_key))) { com_err(progname, kret, "while initializing V4 random key generator"); goto whoops; } (void) des_init_random_number_generator(temp_key.contents); krb5_free_keyblock_contents(rdp->realm_context, &temp_key); #endif rkey_init_done = 1; } whoops: /* * If we choked, then clean up any dirt we may have dropped on the floor. */ if (kret) { finish_realm(rdp); } /* * Solaris Kerberos: * Set the current context back to the general context */ krb5_klog_set_context(kcontext); return(kret); } krb5_sigtype request_exit(int signo) { signal_requests_exit = 1; #ifdef POSIX_SIGTYPE return; #else return(0); #endif } krb5_sigtype request_hup(int signo) { signal_requests_hup = 1; #ifdef POSIX_SIGTYPE return; #else return(0); #endif } void setup_signal_handlers(void) { #ifdef POSIX_SIGNALS (void) sigemptyset(&s_action.sa_mask); s_action.sa_flags = 0; s_action.sa_handler = request_exit; (void) sigaction(SIGINT, &s_action, (struct sigaction *) NULL); (void) sigaction(SIGTERM, &s_action, (struct sigaction *) NULL); s_action.sa_handler = request_hup; (void) sigaction(SIGHUP, &s_action, (struct sigaction *) NULL); s_action.sa_handler = SIG_IGN; (void) sigaction(SIGPIPE, &s_action, (struct sigaction *) NULL); #else /* POSIX_SIGNALS */ signal(SIGINT, request_exit); signal(SIGTERM, request_exit); signal(SIGHUP, request_hup); signal(SIGPIPE, SIG_IGN); #endif /* POSIX_SIGNALS */ return; } krb5_error_code setup_sam(void) { return krb5_c_make_random_key(kdc_context, ENCTYPE_DES_CBC_MD5, &psr_key); } void usage(char *name) { fprintf(stderr, gettext("usage: %s [-d dbpathname] [-r dbrealmname] [-R replaycachename ]\n\t[-m] [-k masterenctype] [-M masterkeyname] [-p port] [-n]\n"), name); fprintf(stderr, "usage: %s [-x db_args]* [-d dbpathname] [-r dbrealmname] [-R replaycachename ]\n\t[-m] [-k masterenctype] [-M masterkeyname] [-p port] [-X] [-n]\n" "\nwhere,\n\t[-x db_args]* - any number of database specific arguments.\n" "\t\t\tLook at each database documentation for supported arguments\n", name); return; } void initialize_realms(krb5_context kcontext, int argc, char **argv) { int c; char *db_name = (char *) NULL; char *mkey_name = (char *) NULL; char *rcname __unused; char *lrealm = NULL; krb5_error_code retval; krb5_enctype menctype = ENCTYPE_UNKNOWN; kdc_realm_t *rdatap; krb5_boolean manual = FALSE; char *default_udp_ports = 0; char *default_tcp_ports = 0; krb5_pointer aprof; const char *hierarchy[3]; char **db_args = NULL; int db_args_size = 0; #ifdef KRB5_KRB4_COMPAT char *v4mode = 0; #endif extern char *optarg; rcname = KDCRCACHE; if (!krb5_aprof_init(DEFAULT_KDC_PROFILE, KDC_PROFILE_ENV, &aprof)) { hierarchy[0] = "kdcdefaults"; hierarchy[1] = "kdc_ports"; hierarchy[2] = (char *) NULL; if (krb5_aprof_get_string(aprof, hierarchy, TRUE, &default_udp_ports)) default_udp_ports = 0; hierarchy[1] = "kdc_tcp_ports"; if (krb5_aprof_get_string(aprof, hierarchy, TRUE, &default_tcp_ports)) default_tcp_ports = 0; hierarchy[1] = "kdc_max_tcp_connections"; if (krb5_aprof_get_int32(aprof, hierarchy, TRUE, &max_tcp_data_connections)) { max_tcp_data_connections = DEFAULT_KDC_TCP_CONNECTIONS; } else if (max_tcp_data_connections < MIN_KDC_TCP_CONNECTIONS) { max_tcp_data_connections = DEFAULT_KDC_TCP_CONNECTIONS; } #ifdef KRB5_KRB4_COMPAT hierarchy[1] = "v4_mode"; if (krb5_aprof_get_string(aprof, hierarchy, TRUE, &v4mode)) v4mode = 0; #endif /* aprof_init can return 0 with aprof == NULL */ if (aprof) krb5_aprof_finish(aprof); } if (default_udp_ports == 0) default_udp_ports = strdup(DEFAULT_KDC_UDP_PORTLIST); if (default_tcp_ports == 0) default_tcp_ports = strdup(DEFAULT_KDC_TCP_PORTLIST); /* * Loop through the option list. Each time we encounter a realm name, * use the previously scanned options to fill in for defaults. */ while ((c = getopt(argc, argv, "x:r:d:mM:k:R:e:p:s:n4:X3")) != -1) { switch(c) { case 'x': db_args_size++; { char **temp = realloc( db_args, sizeof(char*) * (db_args_size+1)); /* one for NULL */ if( temp == NULL ) { /* Solaris Kerberos: Keep error messages consistent */ com_err(argv[0], errno, gettext("while initializing KDC")); exit(1); } db_args = temp; } db_args[db_args_size-1] = optarg; db_args[db_args_size] = NULL; break; case 'r': /* realm name for db */ if (!find_realm_data(optarg, (krb5_ui_4) strlen(optarg))) { if ((rdatap = (kdc_realm_t *) malloc(sizeof(kdc_realm_t)))) { if ((retval = init_realm(kcontext, argv[0], rdatap, optarg, mkey_name, menctype, default_udp_ports, default_tcp_ports, manual, db_args))) { /* Solaris Kerberos: Keep error messages consistent */ com_err(argv[0], retval, gettext("while initializing realm %s"), optarg); exit(1); } kdc_realmlist[kdc_numrealms] = rdatap; kdc_numrealms++; free(db_args), db_args=NULL, db_args_size = 0; } else { /* Solaris Kerberos: Keep error messages consistent */ com_err(argv[0], errno, gettext("while initializing realm %s"), optarg); exit(1); } } break; case 'd': /* pathname for db */ /* now db_name is not a seperate argument. It has to be passed as part of the db_args */ if( db_name == NULL ) { db_name = malloc(sizeof("dbname=") + strlen(optarg)); if( db_name == NULL ) { /* Solaris Kerberos: Keep error messages consistent */ com_err(argv[0], errno, gettext("while initializing KDC")); exit(1); } sprintf( db_name, "dbname=%s", optarg); } db_args_size++; { char **temp = realloc( db_args, sizeof(char*) * (db_args_size+1)); /* one for NULL */ if( temp == NULL ) { /* Solaris Kerberos: Keep error messages consistent */ com_err(argv[0], errno, gettext("while initializing KDC")); exit(1); } db_args = temp; } db_args[db_args_size-1] = db_name; db_args[db_args_size] = NULL; break; case 'm': /* manual type-in of master key */ manual = TRUE; if (menctype == ENCTYPE_UNKNOWN) menctype = ENCTYPE_DES_CBC_CRC; break; case 'M': /* master key name in DB */ mkey_name = optarg; break; case 'n': nofork++; /* don't detach from terminal */ break; case 'k': /* enctype for master key */ /* Solaris Kerberos: Keep error messages consistent */ if (retval = krb5_string_to_enctype(optarg, &menctype)) com_err(argv[0], retval, gettext("while converting %s to an enctype"), optarg); break; case 'R': rcname = optarg; break; case 'p': if (default_udp_ports) free(default_udp_ports); default_udp_ports = strdup(optarg); if (default_tcp_ports) free(default_tcp_ports); default_tcp_ports = strdup(optarg); break; case '4': #ifdef KRB5_KRB4_COMPAT if (v4mode) free(v4mode); v4mode = strdup(optarg); #endif break; case 'X': #ifdef KRB5_KRB4_COMPAT enable_v4_crossrealm(argv[0]); #endif break; case '?': default: usage(argv[0]); exit(1); } } #ifdef KRB5_KRB4_COMPAT /* * Setup the v4 mode */ process_v4_mode(argv[0], v4mode); free(v4mode); #endif /* * Check to see if we processed any realms. */ if (kdc_numrealms == 0) { /* no realm specified, use default realm */ if ((retval = krb5_get_default_realm(kcontext, &lrealm))) { com_err(argv[0], retval, gettext("while attempting to retrieve default realm")); /* Solaris Kerberos: avoid double logging */ #if 0 fprintf (stderr, "%s: %s, %s", argv[0], error_message (retval), gettext("attempting to retrieve default realm\n")); #endif exit(1); } if ((rdatap = (kdc_realm_t *) malloc(sizeof(kdc_realm_t)))) { if ((retval = init_realm(kcontext, argv[0], rdatap, lrealm, mkey_name, menctype, default_udp_ports, default_tcp_ports, manual, db_args))) { /* Solaris Kerberos: Keep error messages consistent */ com_err(argv[0], retval, gettext("while initializing realm %s"), lrealm); exit(1); } kdc_realmlist[0] = rdatap; kdc_numrealms++; } else { if (lrealm) free(lrealm); } } #ifdef USE_RCACHE /* * Now handle the replay cache. */ if ((retval = kdc_initialize_rcache(kcontext, rcname))) { com_err(argv[0], retval, gettext("while initializing KDC replay cache '%s'"), rcname); exit(1); } #endif /* Ensure that this is set for our first request. */ kdc_active_realm = kdc_realmlist[0]; if (default_udp_ports) free(default_udp_ports); if (default_tcp_ports) free(default_tcp_ports); if (db_args) free(db_args); if (db_name) free(db_name); return; } void finish_realms(char *prog) { int i; for (i = 0; i < kdc_numrealms; i++) { finish_realm(kdc_realmlist[i]); kdc_realmlist[i] = 0; } } /* outline: process args & setup initialize database access (fetch master key, open DB) initialize network loop: listen for packet determine packet type, dispatch to handling routine (AS or TGS (or V4?)) reflect response exit on signal clean up secrets, close db shut down network exit */ int main(int argc, char **argv) { krb5_error_code retval; krb5_context kcontext; int errout = 0; krb5_boolean log_stderr_set; (void) setlocale(LC_ALL, ""); #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ #define TEXT_DOMAIN "KRB5KDC_TEST" /* Use this only if it weren't */ #endif (void) textdomain(TEXT_DOMAIN); if (strrchr(argv[0], '/')) argv[0] = strrchr(argv[0], '/')+1; if (!(kdc_realmlist = (kdc_realm_t **) malloc(sizeof(kdc_realm_t *) * KRB5_KDC_MAX_REALMS))) { fprintf(stderr, gettext("%s: cannot get memory for realm list\n"), argv[0]); exit(1); } memset((char *) kdc_realmlist, 0, (size_t) (sizeof(kdc_realm_t *) * KRB5_KDC_MAX_REALMS)); /* * A note about Kerberos contexts: This context, "kcontext", is used * for the KDC operations, i.e. setup, network connection and error * reporting. The per-realm operations use the "realm_context" * associated with each realm. */ retval = krb5int_init_context_kdc(&kcontext); if (retval) { com_err(argv[0], retval, gettext("while initializing krb5")); exit(1); } krb5_klog_init(kcontext, "kdc", argv[0], 1); /* * Solaris Kerberos: * In the early stages of krb5kdc it is desirable to log error messages * to stderr as well as any other logging locations specified in config * files. */ log_stderr_set = krb5_klog_logging_to_stderr(); if (log_stderr_set != TRUE) { krb5_klog_add_stderr(); } /* initialize_kdc5_error_table(); SUNWresync121 XXX */ /* * Scan through the argument list */ initialize_realms(kcontext, argc, argv); setup_signal_handlers(); load_preauth_plugins(kcontext); retval = setup_sam(); if (retval) { com_err(argv[0], retval, gettext("while initializing SAM")); finish_realms(argv[0]); return 1; } if ((retval = setup_network(argv[0]))) { com_err(argv[0], retval, gettext("while initializing network")); finish_realms(argv[0]); return 1; } /* Solaris Kerberos: Remove the extra stderr logging */ if (log_stderr_set != TRUE) krb5_klog_remove_stderr(); /* * Solaris Kerberos: * List the logs (FILE, STDERR, etc) which are currently being * logged to and print that to stderr. Useful when trying to * track down a failure via SMF. */ if (retval = krb5_klog_list_logs(argv[0])) { com_err(argv[0], retval, gettext("while listing logs")); if (log_stderr_set != TRUE) { fprintf(stderr, gettext("%s: %s while listing logs\n"), argv[0], error_message(retval)); } } if (!nofork && daemon(0, 0)) { com_err(argv[0], errno, gettext("while detaching from tty")); if (log_stderr_set != TRUE) { fprintf(stderr, gettext("%s: %s while detaching from tty\n"), argv[0], strerror(errno)); } finish_realms(argv[0]); return 1; } if (retval = krb5_klog_syslog(LOG_INFO, "commencing operation")) { com_err(argv[0], retval, gettext("while logging message")); errout++; }; if ((retval = listen_and_process(argv[0]))) { com_err(argv[0], retval, gettext("while processing network requests")); errout++; } if ((retval = closedown_network(argv[0]))) { com_err(argv[0], retval, gettext("while shutting down network")); errout++; } krb5_klog_syslog(LOG_INFO, "shutting down"); unload_preauth_plugins(kcontext); krb5_klog_close(kdc_context); finish_realms(argv[0]); if (kdc_realmlist) free(kdc_realmlist); #ifdef USE_RCACHE (void) krb5_rc_close(kcontext, kdc_rcache); #endif #ifndef NOCACHE kdc_free_lookaside(kcontext); #endif krb5_free_context(kcontext); return errout; } /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * kdc/network.c * * Copyright 1990,2000 by the Massachusetts Institute of Technology. * * 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. * * * Network code for Kerberos v5 KDC. */ #include "k5-int.h" #include "com_err.h" #include "kdc_util.h" #include "extern.h" #include "kdc5_err.h" #include "adm_proto.h" #include #include #include #include #include "port-sockets.h" /* #include "socket-utils.h" */ #ifdef HAVE_NETINET_IN_H #include #include #include #ifdef HAVE_SYS_SOCKIO_H /* for SIOCGIFCONF, etc. */ #include #endif #include #include #if HAVE_SYS_SELECT_H #include #endif #include #include #include #ifndef ARPHRD_ETHER /* OpenBSD breaks on multiple inclusions */ #include #endif #ifdef HAVE_SYS_FILIO_H #include /* FIONBIO */ #endif #include "fake-addrinfo.h" /* Misc utility routines. */ static void set_sa_port(struct sockaddr *addr, int port) { switch (addr->sa_family) { case AF_INET: sa2sin(addr)->sin_port = port; break; #ifdef KRB5_USE_INET6 case AF_INET6: sa2sin6(addr)->sin6_port = port; break; #endif default: break; } } static int ipv6_enabled() { #ifdef KRB5_USE_INET6 static int result = -1; if (result == -1) { int s; s = socket(AF_INET6, SOCK_STREAM, 0); if (s >= 0) { result = 1; close(s); } else result = 0; } return result; #else return 0; #endif } static int setreuseaddr(int sock, int value) { return setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value)); } #if defined(KRB5_USE_INET6) && defined(IPV6_V6ONLY) static int setv6only(int sock, int value) { return setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &value, sizeof(value)); } #endif static const char *paddr (struct sockaddr *sa) { static char buf[100]; char portbuf[10]; if (getnameinfo(sa, socklen(sa), buf, sizeof(buf), portbuf, sizeof(portbuf), NI_NUMERICHOST|NI_NUMERICSERV)) strcpy(buf, ""); else { unsigned int len = sizeof(buf) - strlen(buf); char *p = buf + strlen(buf); if (len > 2+strlen(portbuf)) { *p++ = '.'; len--; strncpy(p, portbuf, len); } } return buf; } /* KDC data. */ enum kdc_conn_type { CONN_UDP, CONN_TCP_LISTENER, CONN_TCP }; /* Per-connection info. */ struct connection { int fd; enum kdc_conn_type type; void (*service)(struct connection *, const char *, int); /* Solaris Kerberos: for auditing */ in_port_t port; /* local port */ union { /* Type-specific information. */ struct { int x; } udp; struct { int x; } tcp_listener; struct { /* connection */ struct sockaddr_storage addr_s; socklen_t addrlen; char addrbuf[56]; krb5_fulladdr faddr; krb5_address kaddr; /* incoming */ size_t bufsiz; size_t offset; char *buffer; size_t msglen; /* outgoing */ krb5_data *response; unsigned char lenbuf[4]; sg_buf sgbuf[2]; sg_buf *sgp; int sgnum; /* crude denial-of-service avoidance support */ time_t start_time; } tcp; } u; }; #define SET(TYPE) struct { TYPE *data; int n, max; } /* Start at the top and work down -- this should allow for deletions without disrupting the iteration, since we delete by overwriting the element to be removed with the last element. */ #define FOREACH_ELT(set,idx,vvar) \ for (idx = set.n-1; idx >= 0 && (vvar = set.data[idx], 1); idx--) #define GROW_SET(set, incr, tmpptr) \ (((int)(set.max + incr) < set.max \ || (((size_t)((int)(set.max + incr) * sizeof(set.data[0])) \ / sizeof(set.data[0])) \ != (set.max + incr))) \ ? 0 /* overflow */ \ : ((tmpptr = realloc(set.data, \ (int)(set.max + incr) * sizeof(set.data[0]))) \ ? (set.data = tmpptr, set.max += incr, 1) \ : 0)) /* 1 = success, 0 = failure */ #define ADD(set, val, tmpptr) \ ((set.n < set.max || GROW_SET(set, 10, tmpptr)) \ ? (set.data[set.n++] = val, 1) \ : 0) #define DEL(set, idx) \ (set.data[idx] = set.data[--set.n]) #define FREE_SET_DATA(set) if(set.data) free(set.data); \ (set.data = 0, set.max = 0) /* Set connections; */ static SET(struct connection *) connections; #define n_sockets connections.n #define conns connections.data /* Set udp_port_data, tcp_port_data; */ static SET(u_short) udp_port_data, tcp_port_data; #include "cm.h" static struct select_state sstate; static krb5_error_code add_udp_port(int port) { int i; void *tmp; u_short val; u_short s_port = port; if (s_port != port) return EINVAL; FOREACH_ELT (udp_port_data, i, val) if (s_port == val) return 0; if (!ADD(udp_port_data, s_port, tmp)) return ENOMEM; return 0; } static krb5_error_code add_tcp_port(int port) { int i; void *tmp; u_short val; u_short s_port = port; if (s_port != port) return EINVAL; FOREACH_ELT (tcp_port_data, i, val) if (s_port == val) return 0; if (!ADD(tcp_port_data, s_port, tmp)) return ENOMEM; return 0; } #define USE_AF AF_INET #define USE_TYPE SOCK_DGRAM #define USE_PROTO 0 #define SOCKET_ERRNO errno #include "foreachaddr.h" struct socksetup { const char *prog; krb5_error_code retval; }; static struct connection * add_fd (struct socksetup *data, int sock, enum kdc_conn_type conntype, void (*service)(struct connection *, const char *, int)) { struct connection *newconn; void *tmp; newconn = malloc(sizeof(*newconn)); if (newconn == 0) { data->retval = errno; com_err(data->prog, errno, gettext("cannot allocate storage for connection info")); return 0; } if (!ADD(connections, newconn, tmp)) { data->retval = errno; com_err(data->prog, data->retval, gettext("cannot save socket info")); free(newconn); return 0; } memset(newconn, 0, sizeof(*newconn)); newconn->type = conntype; newconn->fd = sock; newconn->service = service; return newconn; } static void process_packet(struct connection *, const char *, int); static void accept_tcp_connection(struct connection *, const char *, int); static void process_tcp_connection(struct connection *, const char *, int); static struct connection * add_udp_fd (struct socksetup *data, int sock) { return add_fd(data, sock, CONN_UDP, process_packet); } static struct connection * add_tcp_listener_fd (struct socksetup *data, int sock) { return add_fd(data, sock, CONN_TCP_LISTENER, accept_tcp_connection); } static struct connection * add_tcp_data_fd (struct socksetup *data, int sock) { return add_fd(data, sock, CONN_TCP, process_tcp_connection); } static void delete_fd (struct connection *xconn) { struct connection *conn; int i; FOREACH_ELT(connections, i, conn) if (conn == xconn) { DEL(connections, i); break; } free(xconn); } static int setnbio(int sock) { static const int one = 1; return ioctlsocket(sock, FIONBIO, (const void *)&one); } static int setnolinger(int s) { static const struct linger ling = { 0, 0 }; return setsockopt(s, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling)); } /* Returns -1 or socket fd. */ static int setup_a_tcp_listener(struct socksetup *data, struct sockaddr *addr) { int sock; sock = socket(addr->sa_family, SOCK_STREAM, 0); if (sock == -1) { com_err(data->prog, errno, gettext("Cannot create TCP server socket on %s"), paddr(addr)); return -1; } /* * Solaris Kerberos: noticed that there where bind problems for tcp sockets * if kdc restarted quickly. Setting SO_REUSEADDR allowed binds to succeed. */ if (setreuseaddr(sock, 1) < 0) { com_err(data->prog, errno, gettext("enabling SO_REUSEADDR on TCP socket")); close(sock); return -1; } if (bind(sock, addr, socklen(addr)) == -1) { com_err(data->prog, errno, gettext("Cannot bind TCP server socket on %s"), paddr(addr)); close(sock); return -1; } if (listen(sock, 5) < 0) { com_err(data->prog, errno, gettext("Cannot listen on TCP server socket on %s"), paddr(addr)); close(sock); return -1; } if (setnbio(sock)) { com_err(data->prog, errno, gettext("cannot set listening tcp socket on %s non-blocking"), paddr(addr)); close(sock); return -1; } if (setnolinger(sock)) { com_err(data->prog, errno, gettext("disabling SO_LINGER on TCP socket on %s"), paddr(addr)); close(sock); return -1; } return sock; } static int setup_tcp_listener_ports(struct socksetup *data) { struct sockaddr_in sin4; #ifdef KRB5_USE_INET6 struct sockaddr_in6 sin6; #endif int i, port; memset(&sin4, 0, sizeof(sin4)); sin4.sin_family = AF_INET; #ifdef HAVE_SA_LEN sin4.sin_len = sizeof(sin4); #endif sin4.sin_addr.s_addr = INADDR_ANY; #ifdef KRB5_USE_INET6 memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6; #ifdef SIN6_LEN sin6.sin6_len = sizeof(sin6); #endif sin6.sin6_addr = in6addr_any; #endif FOREACH_ELT (tcp_port_data, i, port) { int s4, s6; set_sa_port((struct sockaddr *)&sin4, htons(port)); if (!ipv6_enabled()) { s4 = setup_a_tcp_listener(data, (struct sockaddr *)&sin4); if (s4 < 0) return -1; s6 = -1; } else { #ifndef KRB5_USE_INET6 abort(); #else s4 = s6 = -1; set_sa_port((struct sockaddr *)&sin6, htons(port)); s6 = setup_a_tcp_listener(data, (struct sockaddr *)&sin6); if (s6 < 0) return -1; #ifdef IPV6_V6ONLY if (setv6only(s6, 0)) com_err(data->prog, errno, gettext("setsockopt(IPV6_V6ONLY,0) failed")); #endif s4 = setup_a_tcp_listener(data, (struct sockaddr *)&sin4); #endif /* KRB5_USE_INET6 */ } /* Sockets are created, prepare to listen on them. */ if (s4 >= 0) { FD_SET(s4, &sstate.rfds); if (s4 >= sstate.max) sstate.max = s4 + 1; if (add_tcp_listener_fd(data, s4) == 0) close(s4); else krb5_klog_syslog(LOG_INFO, "listening on fd %d: tcp %s", s4, paddr((struct sockaddr *)&sin4)); } #ifdef KRB5_USE_INET6 if (s6 >= 0) { FD_SET(s6, &sstate.rfds); if (s6 >= sstate.max) sstate.max = s6 + 1; if (add_tcp_listener_fd(data, s6) == 0) { close(s6); s6 = -1; } else krb5_klog_syslog(LOG_INFO, "listening on fd %d: tcp %s", s6, paddr((struct sockaddr *)&sin6)); if (s4 < 0) krb5_klog_syslog(LOG_INFO, "assuming IPv6 socket accepts IPv4"); } #endif } return 0; } static int setup_udp_port(void *P_data, struct sockaddr *addr) { struct socksetup *data = P_data; int sock = -1, i; char haddrbuf[NI_MAXHOST]; int err; u_short port; err = getnameinfo(addr, socklen(addr), haddrbuf, sizeof(haddrbuf), 0, 0, NI_NUMERICHOST); if (err) strcpy(haddrbuf, ""); switch (addr->sa_family) { case AF_INET: break; #ifdef AF_INET6 case AF_INET6: #ifdef KRB5_USE_INET6 break; #else { static int first = 1; if (first) { krb5_klog_syslog (LOG_INFO, "skipping local ipv6 addresses"); first = 0; } return 0; } #endif #endif #ifdef AF_LINK /* some BSD systems, AIX */ case AF_LINK: return 0; #endif #ifdef AF_DLI /* Direct Link Interface - DEC Ultrix/OSF1 link layer? */ case AF_DLI: return 0; #endif default: krb5_klog_syslog (LOG_INFO, "skipping unrecognized local address family %d", addr->sa_family); return 0; } FOREACH_ELT (udp_port_data, i, port) { sock = socket (addr->sa_family, SOCK_DGRAM, 0); if (sock == -1) { data->retval = errno; com_err(data->prog, data->retval, gettext("Cannot create server socket for port %d address %s"), port, haddrbuf); return 1; } set_sa_port(addr, htons(port)); if (bind (sock, (struct sockaddr *)addr, socklen (addr)) == -1) { data->retval = errno; com_err(data->prog, data->retval, gettext("Cannot bind server socket to port %d address %s"), port, haddrbuf); return 1; } FD_SET (sock, &sstate.rfds); if (sock >= sstate.max) sstate.max = sock + 1; krb5_klog_syslog (LOG_INFO, "listening on fd %d: udp %s", sock, paddr((struct sockaddr *)addr)); if (add_udp_fd (data, sock) == 0) return 1; } return 0; } #if 1 static void klog_handler(const void *data, size_t len) { static char buf[BUFSIZ]; static int bufoffset; void *p; #define flush_buf() \ (bufoffset \ ? (((buf[0] == 0 || buf[0] == '\n') \ ? (fork()==0?abort():(void)0) \ : (void)0), \ krb5_klog_syslog(LOG_INFO, "%s", buf), \ memset(buf, 0, sizeof(buf)), \ bufoffset = 0) \ : 0) p = memchr(data, 0, len); if (p) len = (const char *)p - (const char *)data; scan_for_newlines: if (len == 0) return; p = memchr(data, '\n', len); if (p) { if (p != data) klog_handler(data, (size_t)((const char *)p - (const char *)data)); flush_buf(); len -= ((const char *)p - (const char *)data) + 1; data = 1 + (const char *)p; goto scan_for_newlines; } else if (len > sizeof(buf) - 1 || len + bufoffset > sizeof(buf) - 1) { size_t x = sizeof(buf) - len - 1; klog_handler(data, x); flush_buf(); len -= x; data = (const char *)data + x; goto scan_for_newlines; } else { memcpy(buf + bufoffset, data, len); bufoffset += len; } } #endif /* XXX */ extern int krb5int_debug_sendto_kdc; extern void (*krb5int_sendtokdc_debug_handler)(const void*, size_t); krb5_error_code setup_network(const char *prog) { struct socksetup setup_data; krb5_error_code retval; char *cp; int i, port; FD_ZERO(&sstate.rfds); FD_ZERO(&sstate.wfds); FD_ZERO(&sstate.xfds); sstate.max = 0; /* krb5int_debug_sendto_kdc = 1; */ krb5int_sendtokdc_debug_handler = klog_handler; /* Handle each realm's ports */ for (i=0; irealm_ports; while (cp && *cp) { if (*cp == ',' || isspace((int) *cp)) { cp++; continue; } port = strtol(cp, &cp, 10); if (cp == 0) break; retval = add_udp_port(port); if (retval) return retval; } cp = kdc_realmlist[i]->realm_tcp_ports; while (cp && *cp) { if (*cp == ',' || isspace((int) *cp)) { cp++; continue; } port = strtol(cp, &cp, 10); if (cp == 0) break; retval = add_tcp_port(port); if (retval) return retval; } } setup_data.prog = prog; setup_data.retval = 0; krb5_klog_syslog (LOG_INFO, "setting up network..."); /* To do: Use RFC 2292 interface (or follow-on) and IPV6_PKTINFO, so we might need only one UDP socket; fall back to binding sockets on each address only if IPV6_PKTINFO isn't supported. */ if (foreach_localaddr (&setup_data, setup_udp_port, 0, 0)) { return setup_data.retval; } setup_tcp_listener_ports(&setup_data); krb5_klog_syslog (LOG_INFO, "set up %d sockets", n_sockets); if (n_sockets == 0) { com_err(prog, 0, gettext("no sockets set up?")); exit (1); } return 0; } static void init_addr(krb5_fulladdr *faddr, struct sockaddr *sa) { switch (sa->sa_family) { case AF_INET: faddr->address->addrtype = ADDRTYPE_INET; faddr->address->length = IPV4_ADDR_LEN; faddr->address->contents = (krb5_octet *) &sa2sin(sa)->sin_addr; faddr->port = ntohs(sa2sin(sa)->sin_port); break; #ifdef KRB5_USE_INET6 case AF_INET6: if (IN6_IS_ADDR_V4MAPPED(&sa2sin6(sa)->sin6_addr)) { faddr->address->addrtype = ADDRTYPE_INET; faddr->address->length = IPV4_ADDR_LEN; /* offset to RAM address of ipv4 part of ipv6 address */ faddr->address->contents = (IPV6_ADDR_LEN - IPV4_ADDR_LEN) + (krb5_octet *) &sa2sin6(sa)->sin6_addr; } else { faddr->address->addrtype = ADDRTYPE_INET6; faddr->address->length = IPV6_ADDR_LEN; faddr->address->contents = (krb5_octet *) &sa2sin6(sa)->sin6_addr; } faddr->port = ntohs(sa2sin6(sa)->sin6_port); break; #endif default: faddr->address->addrtype = -1; faddr->address->length = 0; faddr->address->contents = 0; faddr->port = 0; break; } } static void process_packet(struct connection *conn, const char *prog, int selflags) { int cc; socklen_t saddr_len; krb5_fulladdr faddr; krb5_error_code retval; struct sockaddr_storage saddr; krb5_address addr; krb5_data request; krb5_data *response; char pktbuf[MAX_DGRAM_SIZE]; int port_fd = conn->fd; response = NULL; saddr_len = sizeof(saddr); cc = recvfrom(port_fd, pktbuf, sizeof(pktbuf), 0, (struct sockaddr *)&saddr, &saddr_len); if (cc == -1) { if (errno != EINTR /* This is how Linux indicates that a previous transmission was refused, e.g., if the client timed out before getting the response packet. */ && errno != ECONNREFUSED ) com_err(prog, errno, gettext("while receiving from network")); return; } if (!cc) return; /* zero-length packet? */ request.length = cc; request.data = pktbuf; faddr.address = &addr; init_addr(&faddr, ss2sa(&saddr)); /* this address is in net order */ if ((retval = dispatch(&request, &faddr, &response))) { com_err(prog, retval, gettext("while dispatching (udp)")); return; } cc = sendto(port_fd, response->data, (socklen_t) response->length, 0, (struct sockaddr *)&saddr, saddr_len); if (cc == -1) { char addrbuf[46]; krb5_free_data(kdc_context, response); if (inet_ntop(((struct sockaddr *)&saddr)->sa_family, addr.contents, addrbuf, sizeof(addrbuf)) == 0) { strcpy(addrbuf, "?"); } com_err(prog, errno, gettext("while sending reply to %s/%d"), addrbuf, faddr.port); return; } if (cc != response->length) { krb5_free_data(kdc_context, response); com_err(prog, 0, gettext("short reply write %d vs %d\n"), response->length, cc); return; } krb5_free_data(kdc_context, response); return; } static int tcp_data_counter; /* Solaris kerberos: getting this value from elsewhere */ extern int max_tcp_data_connections; static void kill_tcp_connection(struct connection *); static void accept_tcp_connection(struct connection *conn, const char *prog, int selflags) { int s; struct sockaddr_storage addr_s; struct sockaddr *addr = (struct sockaddr *)&addr_s; socklen_t addrlen = sizeof(addr_s); struct socksetup sockdata; struct connection *newconn; char tmpbuf[10]; s = accept(conn->fd, addr, &addrlen); if (s < 0) return; setnbio(s), setnolinger(s); sockdata.prog = prog; sockdata.retval = 0; newconn = add_tcp_data_fd(&sockdata, s); if (newconn == 0) return; if (getnameinfo((struct sockaddr *)&addr_s, addrlen, newconn->u.tcp.addrbuf, sizeof(newconn->u.tcp.addrbuf), tmpbuf, sizeof(tmpbuf), NI_NUMERICHOST | NI_NUMERICSERV)) strcpy(newconn->u.tcp.addrbuf, "???"); else { char *p, *end; p = newconn->u.tcp.addrbuf; end = p + sizeof(newconn->u.tcp.addrbuf); p += strlen(p); if (end - p > 2 + strlen(tmpbuf)) { *p++ = '.'; strcpy(p, tmpbuf); } } #if 0 krb5_klog_syslog(LOG_INFO, "accepted TCP connection on socket %d from %s", s, newconn->u.tcp.addrbuf); #endif newconn->u.tcp.addr_s = addr_s; newconn->u.tcp.addrlen = addrlen; newconn->u.tcp.bufsiz = 1024 * 1024; newconn->u.tcp.buffer = malloc(newconn->u.tcp.bufsiz); newconn->u.tcp.start_time = time(0); if (++tcp_data_counter > max_tcp_data_connections) { struct connection *oldest_tcp = NULL; struct connection *c; int i; krb5_klog_syslog(LOG_INFO, "too many connections"); FOREACH_ELT (connections, i, c) { if (c->type != CONN_TCP) continue; if (c == newconn) continue; #if 0 krb5_klog_syslog(LOG_INFO, "fd %d started at %ld", c->fd, c->u.tcp.start_time); #endif if (oldest_tcp == NULL || oldest_tcp->u.tcp.start_time > c->u.tcp.start_time) oldest_tcp = c; } if (oldest_tcp != NULL) { krb5_klog_syslog(LOG_INFO, "dropping tcp fd %d from %s", oldest_tcp->fd, oldest_tcp->u.tcp.addrbuf); kill_tcp_connection(oldest_tcp); oldest_tcp = NULL; } } if (newconn->u.tcp.buffer == 0) { com_err(prog, errno, gettext("allocating buffer for new TCP session from %s"), newconn->u.tcp.addrbuf); delete_fd(newconn); close(s); tcp_data_counter--; return; } newconn->u.tcp.offset = 0; newconn->u.tcp.faddr.address = &newconn->u.tcp.kaddr; init_addr(&newconn->u.tcp.faddr, ss2sa(&newconn->u.tcp.addr_s)); SG_SET(&newconn->u.tcp.sgbuf[0], newconn->u.tcp.lenbuf, 4); SG_SET(&newconn->u.tcp.sgbuf[1], 0, 0); FD_SET(s, &sstate.rfds); if (sstate.max <= s) sstate.max = s + 1; } static void kill_tcp_connection(struct connection *conn) { if (conn->u.tcp.response) krb5_free_data(kdc_context, conn->u.tcp.response); if (conn->u.tcp.buffer) free(conn->u.tcp.buffer); FD_CLR(conn->fd, &sstate.rfds); FD_CLR(conn->fd, &sstate.wfds); if (sstate.max == conn->fd + 1) while (sstate.max > 0 && ! FD_ISSET(sstate.max-1, &sstate.rfds) && ! FD_ISSET(sstate.max-1, &sstate.wfds) /* && ! FD_ISSET(sstate.max-1, &sstate.xfds) */ ) sstate.max--; close(conn->fd); conn->fd = -1; delete_fd(conn); tcp_data_counter--; } static krb5_error_code make_toolong_error (krb5_data **out) { krb5_error errpkt; krb5_error_code retval; krb5_data *scratch; retval = krb5_us_timeofday(kdc_context, &errpkt.stime, &errpkt.susec); if (retval) return retval; errpkt.error = KRB_ERR_FIELD_TOOLONG; errpkt.server = tgs_server; errpkt.client = NULL; errpkt.cusec = 0; errpkt.ctime = 0; errpkt.text.length = 0; errpkt.text.data = 0; errpkt.e_data.length = 0; errpkt.e_data.data = 0; scratch = malloc(sizeof(*scratch)); if (scratch == NULL) return ENOMEM; retval = krb5_mk_error(kdc_context, &errpkt, scratch); if (retval) { free(scratch); return retval; } *out = scratch; return 0; } static void process_tcp_connection(struct connection *conn, const char *prog, int selflags) { if (selflags & SSF_WRITE) { ssize_t nwrote; SOCKET_WRITEV_TEMP tmp; nwrote = SOCKET_WRITEV(conn->fd, conn->u.tcp.sgp, conn->u.tcp.sgnum, tmp); if (nwrote < 0) { goto kill_tcp_connection; } if (nwrote == 0) /* eof */ goto kill_tcp_connection; while (nwrote) { sg_buf *sgp = conn->u.tcp.sgp; if (nwrote < SG_LEN(sgp)) { SG_ADVANCE(sgp, nwrote); nwrote = 0; } else { nwrote -= SG_LEN(sgp); conn->u.tcp.sgp++; conn->u.tcp.sgnum--; if (conn->u.tcp.sgnum == 0 && nwrote != 0) abort(); } } if (conn->u.tcp.sgnum == 0) { /* finished sending */ /* We should go back to reading, though if we sent a FIELD_TOOLONG error in reply to a length with the high bit set, RFC 4120 says we have to close the TCP stream. */ goto kill_tcp_connection; } } else if (selflags & SSF_READ) { /* Read message length and data into one big buffer, already allocated at connect time. If we have a complete message, we stop reading, so we should only be here if there is no data in the buffer, or only an incomplete message. */ size_t len; ssize_t nread; if (conn->u.tcp.offset < 4) { /* msglen has not been computed */ /* XXX Doing at least two reads here, letting the kernel worry about buffering. It'll be faster when we add code to manage the buffer here. */ len = 4 - conn->u.tcp.offset; nread = SOCKET_READ(conn->fd, conn->u.tcp.buffer + conn->u.tcp.offset, len); if (nread < 0) /* error */ goto kill_tcp_connection; if (nread == 0) /* eof */ goto kill_tcp_connection; conn->u.tcp.offset += nread; if (conn->u.tcp.offset == 4) { unsigned char *p = (unsigned char *)conn->u.tcp.buffer; conn->u.tcp.msglen = ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]); if (conn->u.tcp.msglen > conn->u.tcp.bufsiz - 4) { krb5_error_code err; /* message too big */ krb5_klog_syslog(LOG_ERR, "TCP client %s wants %lu bytes, cap is %lu", conn->u.tcp.addrbuf, (unsigned long) conn->u.tcp.msglen, (unsigned long) conn->u.tcp.bufsiz - 4); /* XXX Should return an error. */ err = make_toolong_error (&conn->u.tcp.response); if (err) { krb5_klog_syslog(LOG_ERR, "error constructing KRB_ERR_FIELD_TOOLONG error! %s", error_message(err)); goto kill_tcp_connection; } goto have_response; } } } else { /* msglen known */ krb5_data request; krb5_error_code err; len = conn->u.tcp.msglen - (conn->u.tcp.offset - 4); nread = SOCKET_READ(conn->fd, conn->u.tcp.buffer + conn->u.tcp.offset, len); if (nread < 0) /* error */ goto kill_tcp_connection; if (nread == 0) /* eof */ goto kill_tcp_connection; conn->u.tcp.offset += nread; if (conn->u.tcp.offset < conn->u.tcp.msglen + 4) return; /* have a complete message, and exactly one message */ request.length = conn->u.tcp.msglen; request.data = conn->u.tcp.buffer + 4; err = dispatch(&request, &conn->u.tcp.faddr, &conn->u.tcp.response); if (err) { com_err(prog, err, gettext("while dispatching (tcp)")); goto kill_tcp_connection; } have_response: conn->u.tcp.lenbuf[0] = 0xff & (conn->u.tcp.response->length >> 24); conn->u.tcp.lenbuf[1] = 0xff & (conn->u.tcp.response->length >> 16); conn->u.tcp.lenbuf[2] = 0xff & (conn->u.tcp.response->length >> 8); conn->u.tcp.lenbuf[3] = 0xff & (conn->u.tcp.response->length >> 0); SG_SET(&conn->u.tcp.sgbuf[1], conn->u.tcp.response->data, conn->u.tcp.response->length); conn->u.tcp.sgp = conn->u.tcp.sgbuf; conn->u.tcp.sgnum = 2; FD_CLR(conn->fd, &sstate.rfds); FD_SET(conn->fd, &sstate.wfds); } } else abort(); return; kill_tcp_connection: kill_tcp_connection(conn); } static void service_conn(struct connection *conn, const char *prog, int selflags) { conn->service(conn, prog, selflags); } krb5_error_code listen_and_process(const char *prog) { int nfound; /* This struct contains 3 fd_set objects; on some platforms, they can be rather large. Making this static avoids putting all that junk on the stack. */ static struct select_state sout; int i, sret; krb5_error_code err; if (conns == (struct connection **) NULL) return KDC5_NONET; while (!signal_requests_exit) { if (signal_requests_hup) { krb5_klog_reopen(kdc_context); signal_requests_hup = 0; } sstate.end_time.tv_sec = sstate.end_time.tv_usec = 0; err = krb5int_cm_call_select(&sstate, &sout, &sret); if (err) { com_err(prog, err, gettext("while selecting for network input(1)")); continue; } if (sret == -1) { if (errno != EINTR) com_err(prog, errno, gettext("while selecting for network input(2)")); continue; } nfound = sret; for (i=0; i 0; i++) { int sflags = 0; if (conns[i]->fd < 0) abort(); if (FD_ISSET(conns[i]->fd, &sout.rfds)) sflags |= SSF_READ, nfound--; if (FD_ISSET(conns[i]->fd, &sout.wfds)) sflags |= SSF_WRITE, nfound--; if (sflags) service_conn(conns[i], prog, sflags); } } return 0; } krb5_error_code closedown_network(const char *prog) { int i; struct connection *conn; if (conns == (struct connection **) NULL) return KDC5_NONET; FOREACH_ELT (connections, i, conn) { if (conn->fd >= 0) (void) close(conn->fd); DEL (connections, i); /* There may also be per-connection data in the tcp structure (tcp.buffer, tcp.response) that we're not freeing here. That should only happen if we quit with a connection in progress. */ free(conn); } FREE_SET_DATA(connections); FREE_SET_DATA(udp_port_data); FREE_SET_DATA(tcp_port_data); return 0; } #endif /* INET */ /* * kdc/policy.c * * Copyright 1990 by the Massachusetts Institute of Technology. * * 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. * * * Policy decision routines for KDC. */ #include "k5-int.h" #include "kdc_util.h" int against_local_policy_as(register krb5_kdc_req *request, krb5_db_entry client, krb5_db_entry server, krb5_timestamp kdc_time, const char **status) { #if 0 /* An AS request must include the addresses field */ if (request->addresses == 0) { *status = "NO ADDRESS"; return KRB5KDC_ERR_POLICY; } #endif return 0; /* not against policy */ } /* * This is where local policy restrictions for the TGS should placed. */ krb5_error_code against_local_policy_tgs(register krb5_kdc_req *request, krb5_db_entry server, krb5_ticket *ticket, const char **status) { #if 0 /* * For example, if your site wants to disallow ticket forwarding, * you might do something like this: */ if (isflagset(request->kdc_options, KDC_OPT_FORWARDED)) { *status = "FORWARD POLICY"; return KRB5KDC_ERR_POLICY; } #endif return 0; /* not against policy */ } /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef __KRB5_KDC_POLICY__ #define __KRB5_KDC_POLICY__ #ifdef __cplusplus extern "C" { #endif /* * kdc/policy.h * * Copyright 1990 by the Massachusetts Institute of Technology. * * 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. * * * Declarations for policy.c */ extern int against_postdate_policy (krb5_timestamp); extern int against_flag_policy_as (const krb5_kdc_req *); extern int against_flag_policy_tgs (const krb5_kdc_req *, const krb5_ticket *); #ifdef __cplusplus } #endif #endif /* !__KRB5_KDC_POLICY__ */ /* * kdc/replay.c * * Copyright 1991 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. * * * Replay lookaside cache for the KDC, to avoid extra work. * */ #include "k5-int.h" #include "kdc_util.h" #include "extern.h" #ifndef NOCACHE typedef struct _krb5_kdc_replay_ent { struct _krb5_kdc_replay_ent *next; int num_hits; krb5_int32 timein; time_t db_age; krb5_data *req_packet; krb5_data *reply_packet; } krb5_kdc_replay_ent; static krb5_kdc_replay_ent root_ptr = {0}; static int hits = 0; static int calls = 0; static int max_hits_per_entry = 0; static int num_entries = 0; #define STALE_TIME 2*60 /* two minutes */ #define STALE(ptr) ((abs((ptr)->timein - timenow) >= STALE_TIME) || \ ((ptr)->db_age != db_age)) #define MATCH(ptr) (((ptr)->req_packet->length == inpkt->length) && \ !memcmp((ptr)->req_packet->data, inpkt->data, \ inpkt->length) && \ ((ptr)->db_age == db_age)) /* XXX Todo: quench the size of the queue... */ /* return TRUE if outpkt is filled in with a packet to reply with, FALSE if the caller should do the work */ krb5_boolean kdc_check_lookaside(krb5_data *inpkt, krb5_data **outpkt) { krb5_int32 timenow; register krb5_kdc_replay_ent *eptr, *last, *hold; time_t db_age; if (krb5_timeofday(kdc_context, &timenow) || krb5_db_get_age(kdc_context, 0, &db_age)) return FALSE; calls++; /* search for a replay entry in the queue, possibly removing stale entries while we're here */ if (root_ptr.next) { for (last = &root_ptr, eptr = root_ptr.next; eptr; eptr = eptr->next) { if (MATCH(eptr)) { eptr->num_hits++; hits++; if (krb5_copy_data(kdc_context, eptr->reply_packet, outpkt)) return FALSE; else return TRUE; /* return here, don't bother flushing even if it is stale. if we just matched, we may get another retransmit... */ } if (STALE(eptr)) { /* flush it and collect stats */ max_hits_per_entry = max(max_hits_per_entry, eptr->num_hits); krb5_free_data(kdc_context, eptr->req_packet); krb5_free_data(kdc_context, eptr->reply_packet); hold = eptr; last->next = eptr->next; eptr = last; free(hold); } else { /* this isn't it, just move along */ last = eptr; } } } return FALSE; } /* insert a request & reply into the lookaside queue. assumes it's not already there, and can fail softly due to other weird errors. */ void kdc_insert_lookaside(krb5_data *inpkt, krb5_data *outpkt) { register krb5_kdc_replay_ent *eptr; krb5_int32 timenow; time_t db_age; if (krb5_timeofday(kdc_context, &timenow) || krb5_db_get_age(kdc_context, 0, &db_age)) return; /* this is a new entry */ eptr = (krb5_kdc_replay_ent *)calloc(1, sizeof(*eptr)); if (!eptr) return; eptr->timein = timenow; eptr->db_age = db_age; /* * This is going to hurt a lot malloc()-wise due to the need to * allocate memory for the krb5_data and krb5_address elements. * ARGH! */ if (krb5_copy_data(kdc_context, inpkt, &eptr->req_packet)) { free(eptr); return; } if (krb5_copy_data(kdc_context, outpkt, &eptr->reply_packet)) { krb5_free_data(kdc_context, eptr->req_packet); free(eptr); return; } eptr->next = root_ptr.next; root_ptr.next = eptr; num_entries++; return; } /* frees memory associated with the lookaside queue for memory profiling */ void kdc_free_lookaside(krb5_context kcontext) { register krb5_kdc_replay_ent *eptr, *last, *hold; if (root_ptr.next) { for (last = &root_ptr, eptr = root_ptr.next; eptr; eptr = eptr->next) { krb5_free_data(kcontext, eptr->req_packet); krb5_free_data(kcontext, eptr->reply_packet); hold = eptr; last->next = eptr->next; eptr = last; free(hold); } } } #endif /* NOCACHE */ /* * kdc/sock2p.c * * Copyright 2000 by the Massachusetts Institute of Technology. * * 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. * * * Network code for Kerberos v5 KDC. */ #define NEED_SOCKETS #include "k5-int.h" #ifdef HAVE_NETINET_IN_H #include #include #include #ifndef HAVE_INET_NTOP char * inet_ntop (int family, const void *address, char *buf, size_t bufsiz) { char *p; switch (family) { case AF_INET: { p = inet_ntoa (*(const struct in_addr *)address); try: if (strlen (p) >= bufsiz) return 0; strcpy (buf, p); break; } #ifdef KRB5_USE_INET6 case AF_INET6: { char abuf[46]; const unsigned char *byte = (const unsigned char *) &((const struct in6_addr *)address)->s6_addr; sprintf (abuf, "%x:%x:%x:%x:%x:%x:%x:%x", byte[0] * 256 + byte[1], byte[2] * 256 + byte[3], byte[4] * 256 + byte[5], byte[6] * 256 + byte[7], byte[8] * 256 + byte[9], byte[10] * 256 + byte[11], byte[12] * 256 + byte[13], byte[14] * 256 + byte[15]); p = abuf; goto try; } #endif /* KRB5_USE_INET6 */ default: return 0; } return buf; } #endif void sockaddr2p (const struct sockaddr *s, char *buf, size_t bufsiz, int *port_p) { const void *addr; int port; switch (s->sa_family) { case AF_INET: addr = &((const struct sockaddr_in *)s)->sin_addr; port = ((const struct sockaddr_in *)s)->sin_port; break; #ifdef KRB5_USE_INET6 case AF_INET6: addr = &((const struct sockaddr_in6 *)s)->sin6_addr; port = ((const struct sockaddr_in6 *)s)->sin6_port; break; #endif default: if (bufsiz >= 2) strcpy (buf, "?"); if (port_p) *port_p = -1; return; } if (inet_ntop (s->sa_family, addr, buf, bufsiz) == 0 && bufsiz >= 2) strcpy (buf, "?"); if (port_p) *port_p = port; } #endif /* INET */ # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Copyright 2020 Joyent, Inc. TESTPROG = kwarndtest PROG= ktkt_warnd MANIFEST= ktkt_warn.xml KWARNDOBJS = kwarnd.o kwarnd_svc.o kwarnd_proc.o kwarnd_generic.o kwarnd_send.o KWARNTOBJS = kwarndtest.o G_OBJS = kwarnd_xdr.o OBJS = $(KWARNDOBJS) $(KWARNTOBJS) $(G_OBJS) SRCS = $(OBJS:.o=.c) RSRC = kwarnd_xdr.c kwarnd_svc.c kwarnd.h CLOBBERFILES += $(TESTPROG) include ../../Makefile.cmd include $(SRC)/lib/gss_mechs/mech_krb5/Makefile.mech_krb5 ROOTMANIFESTDIR= $(ROOTSVCNETWORKSECURITY) POFILE = $(PROG).po POFILES = generic.po CPPFLAGS += -D_REENTRANT CERRWARN += -Wno-unused-variable CERRWARN += -Wno-unused-function CERRWARN += -Wno-implicit-function-declaration CERRWARN += -Wno-parentheses # not linted SMATCH=off CPPFLAGS += -I../../lib/gss_mechs/mech_krb5/include \ -I$(SRC)/lib/gss_mechs/mech_krb5 \ -I$(SRC)/lib/gss_mechs/mech_krb5/include \ -I$(SRC)/uts/common/gssapi/mechs/krb5/include ktkt_warnd : MAPFILES = $(MAPFILE.INT) $(MAPFILE.NGB) ktkt_warnd : LDFLAGS += $(KRUNPATH) $(MAPFILES:%=-Wl,-M%) ktkt_warnd : LDLIBS += -lnsl LDFLAGS += $(KRUNPATH) LDLIBS += $(KMECHLIB) .KEEP_STATE: all: $(PROG) $(TESTPROG) $(PROG): $(G_OBJS) $(KWARNDOBJS) $$(MAPFILES) $(LINK.c) $(KWARNDOBJS) $(G_OBJS) -o $@ $(LDLIBS) $(POST_PROCESS) # the client entry points (kwarn_*_warning) are now avail in mech_krb5.so $(TESTPROG): $(KWARNTOBJS) $(LINK.c) $(KWARNTOBJS) -o $@ $(LDLIBS) $(POST_PROCESS) # Hammerhead: pre-generated (rpcgen + GNU cpp truncation bug) # kwarnd.h, kwarnd_svc.c, kwarnd_xdr.c are now committed source files. $(OBJS): kwarnd.h # note that nightly depends on having all of the derived # .c files built here on 'make install', even though this # module doesn't use all of those files. install: $(KRB5LIBPROG) $(RSRC) $(ROOTMANIFEST) check: $(CHKMANIFEST) clean: $(RM) $(OBJS) @# Hammerhead: do not delete pre-generated $(RSRC) include ../../Makefile.targ #additional dependencies $(LIBRARY) : $(OBJS) $(DYNLIB) : $(PICS) FRC: $(POFILE): $(DERIVED_FILES) .WAIT $(POFILES) $(RM) $@ $(CAT) $(POFILES) > $@ generic.po: FRC $(RM) messages.po $(XGETTEXT) $(XGETFLAGS) `$(GREP) -l gettext *.[ch]` $(SED) "/^domain/d" messages.po > $@ $(RM) messages.po /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Usermode daemon which is responsible for sending kerberos credentials * expiration warnings to the user, syslog or snmp (eventually), depending * on how it is configured through /etc/krb5/warn.conf. * the code in this file was borrowed from gssd.c */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kwarnd.h" #define MAXTHREADS 64 int kwarnd_debug = 0; /* enable debugging printfs */ extern void kwarnprog_1(struct svc_req *, register SVCXPRT *); static void usage(void); static void detachfromtty(void); extern int svc_create_local_service(void (*) (), ulong_t, ulong_t, char *, char *); extern void kwarnd_check_warning_list(void); extern bool_t loadConfigFile(void); /* following declarations needed in rpcgen-generated code */ int _rpcpmstart = 0; /* Started by a port monitor ? */ int _rpcfdtype; /* Whether Stream or Datagram ? */ int _rpcsvcdirty; /* Still serving ? */ mutex_t _svcstate_lock = ERRORCHECKMUTEX; char myhostname[MAXHOSTNAMELEN] = {0}; char progname[MAXNAMELEN] = {0}; int main(int argc, char **argv) { SVCXPRT *transp; extern int optind; int c; char mname[FMNAMESZ + 1]; int rpc_svc_mode = RPC_SVC_MT_AUTO; /* set locale and domain for internationalization */ setlocale(LC_ALL, ""); #if !defined(TEXT_DOMAIN) #define TEXT_DOMAIN "SYS_TEST" #endif textdomain(TEXT_DOMAIN); (void) strlcpy(progname, basename(argv[0]), sizeof (progname)); /* * Take special note that "getuid()" is called here. This call is used * rather that app_krb5_user_uid(), to ensure ktkt_warnd(8) is running * as root. */ #ifdef DEBUG (void) setuid(0); /* DEBUG: set ruid to root */ #endif /* DEBUG */ if (getuid()) { (void) fprintf(stderr, gettext("[%s] must be run as root\n"), argv[0]); #ifdef DEBUG (void) fprintf(stderr, gettext(" warning only\n")); #else /* !DEBUG */ exit(1); #endif /* DEBUG */ } while ((c = getopt(argc, argv, "d")) != -1) switch (c) { case 'd': /* turn on debugging */ kwarnd_debug = 1; break; default: usage(); } if (optind != argc) { usage(); } (void) gethostname(myhostname, sizeof (myhostname)); /* * Started by inetd if name of module just below stream * head is either a sockmod or timod. */ if (!ioctl(0, I_LOOK, mname) && ((strcmp(mname, "sockmod") == 0) || (strcmp(mname, "timod") == 0))) { char *netid; struct netconfig *nconf; openlog("kwarnd", LOG_PID, LOG_DAEMON); if ((netid = getenv("NLSPROVIDER")) == NULL) { netid = "ticotsord"; } if ((nconf = getnetconfigent(netid)) == NULL) { syslog(LOG_ERR, gettext("cannot get transport info")); exit(1); } if (strcmp(mname, "sockmod") == 0) { if (ioctl(0, I_POP, 0) || ioctl(0, I_PUSH, "timod")) { syslog(LOG_ERR, gettext("could not get the " "right module")); exit(1); } } /* XXX - is nconf even needed here? */ if ((transp = svc_tli_create(0, nconf, NULL, 0, 0)) == NULL) { syslog(LOG_ERR, gettext("cannot create server handle")); exit(1); } /* * We use a NULL nconf because KWARNPROG has already been * registered with rpcbind. */ if (!svc_reg(transp, KWARNPROG, KWARNVERS, kwarnprog_1, NULL)) { syslog(LOG_ERR, gettext("unable to register " "(KWARNPROG, KWARNVERS)")); exit(1); } if (nconf) freenetconfigent(nconf); } else { if (!kwarnd_debug) detachfromtty(); openlog("kwarnd", LOG_PID, LOG_DAEMON); if (svc_create_local_service(kwarnprog_1, KWARNPROG, KWARNVERS, "netpath", "kwarnd") == 0) { syslog(LOG_ERR, gettext("unable to create service")); exit(1); } } if (kwarnd_debug) { fprintf(stderr, gettext("kwarnd start: \n")); } (void) signal(SIGCHLD, SIG_IGN); if (thr_create(NULL, 0, (void *(*)(void *))kwarnd_check_warning_list, NULL, THR_DETACHED | THR_DAEMON | THR_NEW_LWP, NULL)) { syslog(LOG_ERR, gettext("unable to create cache_cleanup thread")); exit(1); } if (!loadConfigFile()) { syslog(LOG_ERR, gettext("could not read config file\n")); exit(1); } if (!rpc_control(RPC_SVC_MTMODE_SET, &rpc_svc_mode)) { syslog(LOG_ERR, gettext("unable to set automatic MT mode")); exit(1); } svc_run(); abort(); /*NOTREACHED*/ #ifdef lint return (1); #endif } static void usage(void) { (void) fprintf(stderr, gettext("usage: %s [-d]\n"), progname); exit(1); } /* * detach from tty */ static void detachfromtty(void) { switch (fork()) { case -1: perror(gettext("kwarnd: can not fork")); exit(1); /*NOTREACHED*/ case 0: break; default: exit(0); } /* * Close existing file descriptors, open "/dev/null" as * standard input, output, and error, and detach from * controlling terminal. */ closefrom(0); (void) open("/dev/null", O_RDONLY); (void) open("/dev/null", O_WRONLY); (void) dup(1); (void) setsid(); } /*ARGSUSED*/ int kwarnprog_1_freeresult(SVCXPRT *transport, xdrproc_t xdr_res, caddr_t res) { xdr_free(xdr_res, res); return (1); } /* * Please do not edit this file. * It was generated using rpcgen. */ #ifndef _KWARND_H_RPCGEN #define _KWARND_H_RPCGEN #include #ifndef _KERNEL #include #include #endif /* !_KERNEL */ /* * Copyright 1990-2002 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * RPC protocol information for kwarnd, the usermode daemon that * assists kinit, kdestroy with kwarnapi. It is kwarnd that executes all * kwarnapi calls and sends credential cache expiration warning messages. * * File generated from kwarnd.x */ #include #include #include #include typedef char *WARNING_NAME_T; typedef u_int OM_UINT32; struct kwarn_add_warning_arg { WARNING_NAME_T warning_name; long cred_exp_time; }; typedef struct kwarn_add_warning_arg kwarn_add_warning_arg; struct kwarn_add_warning_res { OM_UINT32 status; }; typedef struct kwarn_add_warning_res kwarn_add_warning_res; struct kwarn_del_warning_arg { WARNING_NAME_T warning_name; }; typedef struct kwarn_del_warning_arg kwarn_del_warning_arg; struct kwarn_del_warning_res { OM_UINT32 status; }; typedef struct kwarn_del_warning_res kwarn_del_warning_res; #define KWARNPROG 100134 #define KWARNVERS 1 #define KWARN_ADD_WARNING 1 extern enum clnt_stat kwarn_add_warning_1(); extern bool_t kwarn_add_warning_1_svc(); #define KWARN_DEL_WARNING 2 extern enum clnt_stat kwarn_del_warning_1(); extern bool_t kwarn_del_warning_1_svc(); extern int kwarnprog_1_freeresult(); /* the xdr functions */ extern bool_t xdr_WARNING_NAME_T(); extern bool_t xdr_OM_UINT32(); extern bool_t xdr_kwarn_add_warning_arg(); extern bool_t xdr_kwarn_add_warning_res(); extern bool_t xdr_kwarn_del_warning_arg(); extern bool_t xdr_kwarn_del_warning_res(); #endif /* !_KWARND_H_RPCGEN */ %/* % * Copyright 1990-2002 Sun Microsystems, Inc. All rights reserved. % * Use is subject to license terms. % */ % %/* % * RPC protocol information for kwarnd, the usermode daemon that % * assists kinit, kdestroy with kwarnapi. It is kwarnd that executes all % * kwarnapi calls and sends credential cache expiration warning messages. % * % * File generated from kwarnd.x % */ % % %#include %#include %#include %#include % /* * These are the definitions for the interface to KWARND. */ #define MAX_PRINCIPAL_LEN 128 typedef string WARNING_NAME_T; typedef unsigned int OM_UINT32; struct kwarn_add_warning_arg { WARNING_NAME_T warning_name; long cred_exp_time; /* time in secs after epoch */ }; struct kwarn_add_warning_res { OM_UINT32 status; /* status of kwarn call */ }; struct kwarn_del_warning_arg { WARNING_NAME_T warning_name; }; struct kwarn_del_warning_res { OM_UINT32 status; /* status of kwarn call */ }; /* * The server accepts requests only from the loopback address. * Unix authentication is used, and the port must be in the reserved range. */ program KWARNPROG { version KWARNVERS { /* * Called by the client to add a cred expiration warning */ kwarn_add_warning_res KWARN_ADD_WARNING(kwarn_add_warning_arg) = 1; /* * Called by the client to delete a cred expiration warning */ kwarn_del_warning_res KWARN_DEL_WARNING(kwarn_del_warning_arg) = 2; } = 1; } = 100134; /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * stub module for kwarnd. */ #include #include #include "kwarnd.h" #include #include #include #include #include #include #include #include #include #include extern CLIENT *getkwarnd_handle(void); extern void resetkwarnd_handle(void); OM_UINT32 kwarn_add_warning(WARNING_NAME_T warning_name, int cred_exp_time) { kwarn_add_warning_arg args; kwarn_add_warning_res res; enum clnt_stat ret; boolean_t first = TRUE; CLIENT *clnt; /* check the input/output parameters */ if (warning_name == NULL || cred_exp_time == 0) return (1); rebind: /* get the client handle to kwarnd */ if ((clnt = getkwarnd_handle()) == NULL) { /* * Let app output if an error occurs but we'll syslog to * DEBUG to get error details if needed. */ syslog(LOG_DEBUG, "%s", clnt_spcreateerror("getkwarnd_handle")); return (1); } /* set the rpc parameters */ args.cred_exp_time = cred_exp_time; args.warning_name = warning_name; /* call the remote procedure */ memset(&res, 0, sizeof (res)); ret = kwarn_add_warning_1(&args, &res, clnt); if (ret != RPC_SUCCESS) { /* * Could have timed out due to the process restarting for * various reasons. Should attempt to rebind in the case * process is actually running. */ if (ret == RPC_TIMEDOUT && first) { resetkwarnd_handle(); first = FALSE; goto rebind; } return (1); } /* nothing to free */ return (res.status); } OM_UINT32 kwarn_del_warning(WARNING_NAME_T warning_name) { kwarn_del_warning_arg args; kwarn_del_warning_res res; enum clnt_stat ret; boolean_t first = TRUE; CLIENT *clnt; /* check the output parameters */ if (warning_name == NULL) return (1); rebind: /* get the client GSSD handle */ if ((clnt = getkwarnd_handle()) == NULL) { /* * Let app output if an error occurs but we'll syslog to * DEBUG to get error details if needed. */ syslog(LOG_DEBUG, "%s", clnt_spcreateerror("getkwarnd_handle")); return (1); } /* set the input parameters */ args.warning_name = warning_name; /* call the remote procedure */ memset(&res, 0, sizeof (res)); ret = kwarn_del_warning_1(&args, &res, clnt); if (ret != RPC_SUCCESS) { /* * Could have timed out due to the process restarting for * various reasons. Should attempt to rebind in the case * process is actually running. */ if (ret == RPC_TIMEDOUT && first) { resetkwarnd_handle(); first = FALSE; goto rebind; } return (1); } /* nothing to free */ return (res.status); } /* * Copyright (c) 1988-1999 by Sun Microsystems, Inc. * All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include extern int __rpc_negotiate_uid(int); /* * The highest level interface for server creation. * Copied from svc_generic.c and cmd/keyserv/key_generic.c, but adapted * to work only for TPI_CLTS semantics, and to be called only once * from kwarnd.c. Returns 1 (interface created) on success and 0 * (no interfaces created) on failure. */ int svc_create_local_service(void (*dispatch) (), /* Dispatch function */ u_long prognum, /* Program number */ u_long versnum, /* Version number */ char *nettype, /* Networktype token */ char *servname) /* name of the srvc */ { int num = 0; SVCXPRT *xprt; struct netconfig *nconf; struct t_bind *bind_addr; void *net; int fd; struct nd_hostserv ns; struct nd_addrlist *nas; if ((net = __rpc_setconf(nettype)) == 0) { (void) syslog(LOG_ERR, gettext("svc_create: could not read netconfig database")); return (0); } while (nconf = __rpc_getconf(net)) { if ((strcmp(nconf->nc_protofmly, NC_LOOPBACK)) || (nconf->nc_semantics != NC_TPI_COTS_ORD)) continue; if ((fd = t_open(nconf->nc_device, O_RDWR, NULL)) < 0) { (void) syslog(LOG_ERR, gettext("svc_create: %s: cannot open connection: %s"), nconf->nc_netid, t_errlist[t_errno]); break; } /* * Negotiate for returning the uid of the caller. * This should be done before enabling the endpoint for * service via t_bind() (called in svc_tli_create()) * so that requests to kwarnd contain the uid. */ if (__rpc_negotiate_uid(fd) != 0) { syslog(LOG_ERR, gettext("Could not negotiate for" " uid with loopback transport %s"), nconf->nc_netid); t_close(fd); break; } /* LINTED pointer alignment */ bind_addr = (struct t_bind *) t_alloc(fd, T_BIND, T_ADDR); if ((bind_addr == NULL)) { (void) t_close(fd); (void) syslog(LOG_ERR, gettext("svc_create: t_alloc failed\n")); break; } ns.h_host = HOST_SELF; ns.h_serv = servname; if (!netdir_getbyname(nconf, &ns, &nas)) { /* Copy the address */ bind_addr->addr.len = nas->n_addrs->len; (void) memcpy(bind_addr->addr.buf, nas->n_addrs->buf, (int) nas->n_addrs->len); bind_addr->qlen = 8; netdir_free((char *) nas, ND_ADDRLIST); } else { (void) syslog(LOG_ERR, gettext("svc_create: no well known " "address for %s on %s\n"), servname, nconf->nc_netid); (void) t_free((char *) bind_addr, T_BIND); bind_addr = NULL; } xprt = svc_tli_create(fd, nconf, bind_addr, 0, 0); if (bind_addr) (void) t_free((char *) bind_addr, T_BIND); if (xprt == NULL) { (void) t_close(fd); (void) syslog(LOG_ERR, gettext("svc_create: svc_tli_create failed\n")); break; } else { (void) rpcb_unset(prognum, versnum, nconf); if (svc_reg(xprt, prognum, versnum, dispatch, nconf) == FALSE) { (void) syslog(LOG_ERR, gettext("svc_create: cannot" " register %d vers %d on %s"), prognum, versnum, nconf->nc_netid); SVC_DESTROY(xprt); /* also t_closes fd */ break; } num = 1; break; } } __rpc_endconf(net); return (num); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* from kerbd_handle.c 1.3 92/01/29 SMI */ /* * kwarnd_handle.c, Interface to kwarnd * */ #include #include #include #include #include #include #include #include "kwarnd.h" #ifdef DEBUG #define dprt(msg) #else #define dprt(msg) #endif /* DEBUG */ CLIENT *kwarn_clnt; /* * Keep the handle cached. This call may be made quite often. */ CLIENT * getkwarnd_handle(void) { void *localhandle; struct netconfig *nconf; struct netconfig *tpconf; struct timeval wait_time; struct utsname u; static char *hostname; static bool_t first_time = TRUE; /* * Total timeout (in seconds) talking to kwarnd. */ #define TOTAL_TIMEOUT 5 if (kwarn_clnt) return (kwarn_clnt); if (!(localhandle = setnetconfig())) return (NULL); tpconf = NULL; if (first_time == TRUE) { if (uname(&u) == -1) { (void) endnetconfig(localhandle); return ((CLIENT *)NULL); } if ((hostname = strdup(u.nodename)) == (char *)NULL) { (void) endnetconfig(localhandle); return ((CLIENT *)NULL); } first_time = FALSE; } while (nconf = getnetconfig(localhandle)) { if (strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0) { if (nconf->nc_semantics == NC_TPI_COTS_ORD) { kwarn_clnt = clnt_tp_create(hostname, KWARNPROG, KWARNVERS, nconf); if (kwarn_clnt) { dprt("got COTS_ORD\n"); break; } } else { tpconf = nconf; } } } if ((kwarn_clnt == NULL) && (tpconf)) { /* Now, try the connection-oriented loopback transport */ kwarn_clnt = clnt_tp_create(hostname, KWARNPROG, KWARNVERS, tpconf); #ifdef DEBUG if (kwarn_clnt) { dprt("got COTS\n"); } #endif /* DEBUG */ } (void) endnetconfig(localhandle); /* * This bit of code uses an as yet unimplemented argument to * clnt_control(). CLSET_SVC_PRIV specifies that the underlying * loopback transport should be checked to ensure it is * connected to a process running as root. If so, the clnt_control() * call returns TRUE. If not, it returns FALSE. */ #ifdef CLSET_SVC_PRIV if (clnt_control(kwarn_clnt, CLSET_SVC_PRIV, NULL) != TRUE) { clnt_destroy(kwarn_clnt); kwarn_clnt = NULL; return (NULL); { #endif if (kwarn_clnt == NULL) return (NULL); kwarn_clnt->cl_auth = authsys_create("", getuid(), 0, 0, NULL); if (kwarn_clnt->cl_auth == NULL) { clnt_destroy(kwarn_clnt); kwarn_clnt = NULL; return (NULL); } wait_time.tv_sec = TOTAL_TIMEOUT; wait_time.tv_usec = 0; (void) clnt_control(kwarn_clnt, CLSET_TIMEOUT, (char *)&wait_time); return (kwarn_clnt); } void resetkwarnd_handle(void) { auth_destroy(kwarn_clnt->cl_auth); clnt_destroy(kwarn_clnt); kwarn_clnt = NULL; } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * RPC server procedures for the usermode daemon kwarnd. */ #include #include #include #include #include #include #include #include #include "kwarnd.h" #include #include #include #include #include #include #include #include #include #include #include #include #include extern char progname[]; struct k5_data { krb5_context ctx; krb5_ccache cc; krb5_principal me; char *name; }; #define MAIL "mail" #define MAILPATH "/usr/bin/mail" #define DEFAULT_CONFIG "* terminal 30m" #define CONF_FILENAME "/etc/krb5/warn.conf" /* warn.conf info */ typedef struct config_entry_s { struct config_entry_s *next; int seconds_to_warn; char *principal; char *where_to; char *email; int renew; int log_success; int log_failure; } config_entry_list_t; static config_entry_list_t *config_entry_list; /* list of principals to be warned */ typedef struct cred_warning_list_s { struct cred_warning_list_s *next; WARNING_NAME_T warn_name; time_t cred_exp_time; time_t cred_warn_time; mutex_t cwm; } cred_warning_list_t; static cred_warning_list_t *cred_warning_list; static rwlock_t cred_lock = DEFAULTRWLOCK; static bool_t del_warning_pvt(char *); static config_entry_list_t * find_warning_info(char *); static bool_t parseConfigLine(char *buffer); extern int warn_send(char *, char *); extern int kwarnd_debug; cred_warning_list_t * find_cred_warning(WARNING_NAME_T warn_name) { cred_warning_list_t *cw; if (!cred_warning_list) return (NULL); for (cw = cred_warning_list; cw != NULL; cw = cw->next) { if (strcmp(warn_name, cw->warn_name) != 0) continue; return (cw); } return (NULL); } /* * add a principal to the principal warning list */ bool_t kwarn_add_warning_1_svc(kwarn_add_warning_arg *argp, kwarn_add_warning_res *res, struct svc_req *rqstp) { cred_warning_list_t *cred_warning; config_entry_list_t *config_entry; if (kwarnd_debug) { printf("kwarn_add_warning_1_svc start; cWlist=%p\n", cred_warning_list); printf("kwarn_add_warning_1_svc: principal %s", argp->warning_name); printf(" exp time: %d\n", argp->cred_exp_time); } /* * if there is no entry in the config file that matches the principal to * be added to the warning list, return true because we are not going to * send a warning for this principal. */ if ((config_entry = find_warning_info(argp->warning_name)) == NULL) { if (kwarnd_debug) printf( "kwarn_add_warning_1_svc find_warn_info: fails, cWlist=%p\n", cred_warning_list); return (TRUE); } /* * see if a warning has already been created for this principal, if so * update the warning time. */ rw_wrlock(&cred_lock); if (cred_warning = find_cred_warning(argp->warning_name)) { rw_unlock(&cred_lock); mutex_lock(&cred_warning->cwm); cred_warning->cred_exp_time = argp->cred_exp_time; cred_warning->cred_warn_time = argp->cred_exp_time - config_entry->seconds_to_warn; mutex_unlock(&cred_warning->cwm); } else { cred_warning = (cred_warning_list_t *)malloc( sizeof (*cred_warning_list)); if (cred_warning == NULL) { rw_unlock(&cred_lock); res->status = 1; return (FALSE); } (void) memset((char *)cred_warning, 0, sizeof (*cred_warning_list)); cred_warning->cred_exp_time = argp->cred_exp_time; cred_warning->cred_warn_time = argp->cred_exp_time - config_entry->seconds_to_warn; cred_warning->warn_name = strdup(argp->warning_name); if (cred_warning->warn_name == NULL) { free(cred_warning); rw_unlock(&cred_lock); res->status = 1; return (FALSE); } mutex_init(&cred_warning->cwm, USYNC_THREAD, NULL); cred_warning->next = cred_warning_list; cred_warning_list = cred_warning; rw_unlock(&cred_lock); } res->status = 0; if (kwarnd_debug) printf( "kwarn_add_warning_1_svc end: returns true; cWlist=%p\n", cred_warning_list); return (TRUE); } /* * delete a warning request for a given principal */ bool_t kwarn_del_warning_1_svc(kwarn_del_warning_arg *argp, kwarn_del_warning_res *res, struct svc_req *rqstp) { if (kwarnd_debug) printf(gettext("delete principal %s requested\n"), argp->warning_name); if (del_warning_pvt(argp->warning_name) == TRUE) { res->status = 0; if (kwarnd_debug) printf(gettext("delete principal %s completed\n"), argp->warning_name); return (TRUE); } else { res->status = 1; if (kwarnd_debug) printf(gettext("delete principal %s failed\n"), argp->warning_name); return (TRUE); } } static bool_t del_warning_pvt(char *warning_name) { cred_warning_list_t *cred_warning, *prev; rw_wrlock(&cred_lock); for (prev = NULL, cred_warning = cred_warning_list; cred_warning != NULL; prev = cred_warning, cred_warning = cred_warning->next) { if (strcmp(cred_warning->warn_name, warning_name) == 0) { if (!prev) cred_warning_list = cred_warning->next; else prev->next = cred_warning->next; free(cred_warning->warn_name); free(cred_warning); rw_unlock(&cred_lock); return (TRUE); } } rw_unlock(&cred_lock); return (FALSE); } /* * load the warn.conf file into the config_entry list. */ bool_t loadConfigFile(void) { char buffer[BUFSIZ]; FILE *cfgfile; bool_t retval = TRUE; if ((cfgfile = fopen(CONF_FILENAME, "r")) == NULL) { syslog(LOG_ERR, gettext( "could not open config file \"%s\"\n"), CONF_FILENAME); syslog(LOG_ERR, gettext( "using default options \"%s\"\n"), DEFAULT_CONFIG); retval = parseConfigLine(DEFAULT_CONFIG); } else { (void) memset(buffer, 0, sizeof (buffer)); while ((fgets(buffer, BUFSIZ, cfgfile) != NULL) && (retval == TRUE)) retval = parseConfigLine(buffer); fclose(cfgfile); } return (retval); } /* * Return TRUE if we get a valid opt and update flags appro. */ static bool_t cmp_renew_opts(char *opt, int *log_success, /* out */ int *log_failure) /* out */ { if (strncasecmp(opt, "log", sizeof ("log")) == 0) { *log_success = *log_failure = 1; } else if (strncasecmp(opt, "log-success", sizeof ("log-success")) == 0) { *log_success = 1; } else if (strncasecmp(opt, "log-failure", sizeof ("log-failure")) == 0) { *log_failure = 1; } else { if (kwarnd_debug) printf("cmp_renew_opts: renew bad opt=`%s'\n", opt ? opt : "null"); return (FALSE); } return (TRUE); } /* * Make the config_entry item for the config_entry_list, based on * buffer. The formats are * * [renew[:]] syslog|terminal