# # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Copyright (c) 2018, Joyent, Inc. PROG1=kprop PROG2=kpropd PROG3=kprop_script PROG=$(PROG1) $(PROG2) $(PROG3) MANIFEST= krb5_prop.xml OBJS = \ kprop.o\ utils.o\ kpropd.o \ kpropd_rpc.o \ iprop_xdr.o COMMONOBJS= utils.o COMMONSRCS= utils.c CLIENTOBJS= kprop.o CLIENTSRCS= kprop.c SERVEROBJS= kpropd.o kpropd_rpc.o SERVERSRCS= kpropd.c kpropd_rpc.c DERIVED_OBJS= iprop_xdr.o DERIVED_SRCS= iprop_xdr.c SRCS = $(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" > $@ DEFS = -DHAVE_LIBSOCKET=1 -DHAVE_LIBNSL=1 \ -DHAVE_COMPILE=1 \ -DHAVE_STEP=1 # Hammerhead: pre-generated files are permanent source, do not delete # CLOBBERFILES += $(RSRC) include ../../Makefile.cmd include $(SRC)/lib/gss_mechs/mech_krb5/Makefile.mech_krb5 ROOTMANIFESTDIR= $(ROOTSVCNETWORKSECURITY) TEXT_DOMAIN = SUNW_OST_OSCMD POFILE = kprop.po POFILES = generic.po CFLAGS += $(CCOPTS) $(DEFS) $(LOCALINCLUDE) CPPFLAGS += -I. \ -I$(KRB5IPROPDIR) \ -I$(SRC)/lib/gss_mechs/mech_krb5/include \ -I$(SRC)/lib/gss_mechs/mech_krb5/include/krb5\ -I$(SRC)/lib/krb5 \ -I$(SRC)/lib/krb5/kadm5 \ -I$(SRC)/uts/common/gssapi/mechs/krb5/include CERRWARN += -Wno-unused-variable CERRWARN += -Wno-unused-function CERRWARN += -Wno-implicit-function-declaration CERRWARN += -Wno-parentheses CERRWARN += -Wno-unused-value CERRWARN += -Wno-address # 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 += -lmech_krb5 -lsocket $(PROG1) : LDLIBS += -L$(KRB5LIB) -lkadm5srv $(PROG2) : LDLIBS += -L$(KRB5LIB) -lnsl -lkdb -lkadm5clnt .KEEP_STATE: all: $(PROG) $(PROG1): $(CLIENTOBJS) $(COMMONOBJS) $(LINK.c) $(CLIENTOBJS) $(COMMONOBJS) -o $@ $(DEPLIBS) $(LDFLAGS) $(LDARGS) $(LDLIBS) $(POST_PROCESS) $(PROG2): $(SERVEROBJS) $(DERIVED_OBJS) $(COMMONOBJS) $(DEPLIBS) $(LINK.c) $(SERVEROBJS) $(DERIVED_OBJS) $(COMMONOBJS) -o $@ $(DEPLIBS) $(LDFLAGS) $(LDARGS) $(LDLIBS) $(POST_PROCESS) # Hammerhead: pre-generated (rpcgen + GNU cpp truncation bug) # iprop.h and iprop_xdr.c are now committed source files. # Make dependencies explicit so make sees it. $(KRB5LIB)/$(PROG3): $(PROG3) $(OBJS): iprop.h install: $(KRB5LIBPROG) $(ROOTSVCMETHOD) $(ROOTMANIFEST) check: $(CHKMANIFEST) install_h: clean: $(RM) $(CLIENTOBJS) $(SERVEROBJS) $(DERIVED_OBJS) $(COMMONOBJS) $(PROG) @# 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 (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. */ /* * slave/kprop.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. * * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "com_err.h" #include "kprop.h" static char *kprop_version = KPROP_PROT_VERSION; char *progname = 0; int debug = 0; char *srvtab = 0; char *slave_host; char *realm = 0; char *file = KPROP_DEFAULT_FILE; short port = 0; krb5_principal my_principal; /* The Kerberos principal we'll be */ /* running under, initialized in */ /* get_tickets() */ krb5_ccache ccache; /* Credentials cache which we'll be using */ krb5_creds creds; krb5_address sender_addr; krb5_address receiver_addr; void PRS (int, char **); void get_tickets (krb5_context); static void usage (void); krb5_error_code open_connection (char *, int *, char *, unsigned int); void kerberos_authenticate (krb5_context, krb5_auth_context *, int, krb5_principal, krb5_creds **); int open_database (krb5_context, char *, int *); void close_database (krb5_context, int); void xmit_database (krb5_context, krb5_auth_context, krb5_creds *, int, int, int); void send_error (krb5_context, krb5_creds *, int, char *, krb5_error_code); void update_last_prop_file (char *, char *); static void usage() { fprintf(stderr, gettext ("\nUsage: %s [-r realm] [-f file] [-d] [-P port] [-s srvtab] slave_host\n\n"), progname); exit(1); } int main(argc, argv) int argc; char **argv; { int fd, database_fd, database_size; krb5_error_code retval; krb5_context context; krb5_creds *my_creds; krb5_auth_context auth_context; #define ERRMSGSIZ 256 char Errmsg[ERRMSGSIZ]; (void) setlocale(LC_ALL, ""); #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ #define TEXT_DOMAIN "KPROP_TEST" /* Use this only if it weren't */ #endif (void) textdomain(TEXT_DOMAIN); retval = krb5_init_context(&context); if (retval) { com_err(argv[0], retval, gettext("while initializing krb5")); exit(1); } PRS(argc, argv); get_tickets(context); database_fd = open_database(context, file, &database_size); retval = open_connection(slave_host, &fd, Errmsg, sizeof(Errmsg)); if (retval) { com_err(progname, retval, gettext("%s while opening connection to %s"), Errmsg, slave_host); exit(1); } if (fd < 0) { fprintf(stderr, gettext("%s: %s while opening connection to %s\n"), progname, Errmsg, slave_host); exit(1); } kerberos_authenticate(context, &auth_context, fd, my_principal, &my_creds); xmit_database(context, auth_context, my_creds, fd, database_fd, database_size); update_last_prop_file(slave_host, file); printf(gettext("Database propagation to %s: SUCCEEDED\n"), slave_host); krb5_free_cred_contents(context, my_creds); close_database(context, database_fd); exit(0); } void PRS(argc, argv) int argc; char **argv; { int c; register char *word, ch; extern int optind; extern char *optarg; progname = argv[0]; while ((c = getopt(argc, argv, "r:f:dP:s:h:")) != EOF) { switch (c) { case 'r': realm = optarg; if (!realm) usage(); break; case 'f': file = optarg; if (!file) usage(); break; case 'd': debug++; break; case 'P': port = atoi(optarg); if (!port) usage(); break; case 's': srvtab = optarg; if (!srvtab) usage(); break; case '?': default: printf (gettext("Error \n")); usage(); } } argc -= optind; argv = &argv[optind]; if (*argv) slave_host = *argv; else usage(); } void get_tickets(context) krb5_context context; { char buf[BUFSIZ]; krb5_error_code retval; static char tkstring[] = "/tmp/kproptktXXXXXX"; krb5_keytab keytab = NULL; krb5_get_init_creds_opt opt; char *svcname = NULL; char *def_realm = NULL; char *master_host = NULL; /* * Figure out what tickets we'll be using to send stuff */ if (realm) { if ((def_realm = strdup(realm)) == NULL) { com_err(progname, ENOMEM, gettext("while allocating default realm name '%s'"), realm); exit(1); } } else { retval = krb5_get_default_realm(context, &def_realm); if (retval) { com_err(progname, retval, gettext("while getting default realm")); exit(1); } } /* * Always pick up the master hostname from krb5.conf, as * opposed to picking up the localhost, so we do not get bit * if the master KDC is HA and hence points to a logicalhost. */ retval = kadm5_get_master(context, def_realm, &master_host); if (retval) { free(def_realm); com_err(progname, retval, gettext("while getting admin server fqdn")); exit(1); } retval = krb5_sname_to_principal(context, master_host, NULL, KRB5_NT_SRV_HST, &my_principal); free(def_realm); free(master_host); if (retval) { com_err(progname, errno, gettext("while setting client principal name")); exit(1); } if (realm) { retval = krb5_set_principal_realm(context, my_principal, realm); if (retval) { com_err(progname, errno, gettext("while setting client principal realm")); exit(1); } } #if 0 krb5_princ_type(context, my_principal) = KRB5_NT_PRINCIPAL; #endif /* * Initialize cache file which we're going to be using */ (void) mktemp(tkstring); snprintf(buf, sizeof (buf), "FILE:%s", tkstring); retval = krb5_cc_resolve(context, buf, &ccache); if (retval) { com_err(progname, retval, gettext("while opening credential cache %s"), buf); exit(1); } retval = krb5_cc_initialize(context, ccache, my_principal); if (retval) { com_err (progname, retval, gettext("when initializing cache %s"), buf); exit(1); } /* * Get the tickets we'll need. * * Construct the principal name for the slave host. */ memset((char *)&creds, 0, sizeof(creds)); retval = krb5_sname_to_principal(context, slave_host, KPROP_SERVICE_NAME, KRB5_NT_SRV_HST, &creds.server); if (retval) { com_err(progname, errno, gettext("while setting server principal name")); (void) krb5_cc_destroy(context, ccache); exit(1); } if (realm) { retval = krb5_set_principal_realm(context, creds.server, realm); if (retval) { com_err(progname, errno, gettext("while setting server principal realm")); exit(1); } } /* * Now fill in the client.... */ retval = krb5_copy_principal(context, my_principal, &creds.client); if (retval) { com_err(progname, retval, gettext("While copying client principal")); (void) krb5_cc_destroy(context, ccache); exit(1); } if (srvtab) { retval = krb5_kt_resolve(context, srvtab, &keytab); if (retval) { com_err(progname, retval, gettext("while resolving keytab")); (void) krb5_cc_destroy(context, ccache); exit(1); } } (void) memset(&opt, 0, sizeof (opt)); krb5_get_init_creds_opt_init(&opt); retval = krb5_unparse_name(context, creds.server, &svcname); if (retval) { com_err(progname, errno, gettext("while parsing svc principal name")); (void) krb5_cc_destroy(context, ccache); exit (1); } retval = krb5_get_init_creds_keytab(context, &creds, creds.client, keytab, 0, svcname, &opt); if (svcname) free(svcname); if (retval) { com_err(progname, retval, gettext("while getting initial ticket\n")); (void) krb5_cc_destroy(context, ccache); exit(1); } if (keytab) (void) krb5_kt_close(context, keytab); /* * Now destroy the cache right away --- the credentials we * need will be in my_creds. */ retval = krb5_cc_destroy(context, ccache); if (retval) { com_err(progname, retval, gettext("while destroying ticket cache")); exit(1); } } /* SUNW14resync - SOCKET is defed in 1.4 in port-sockets.h */ #ifdef SOCKET #undef SOCKET #endif krb5_error_code open_connection(host, fd, Errmsg, ErrmsgSz) char *host; int *fd; char *Errmsg; unsigned int ErrmsgSz; { int s; krb5_error_code retval = 0; int socket_length; struct addrinfo hints, *ai, *aitop; struct sockaddr_storage ss; char serv_or_port[NI_MAXSERV]; enum err_types {SOCKET, CONNECT}; int which_err = -1; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; /* go for either IPv4 or v6 */ hints.ai_socktype = SOCK_STREAM; if (port != 0) (void) snprintf(serv_or_port, sizeof(serv_or_port), ("%hu"), port); else strncpy(serv_or_port, KPROP_SERVICE, sizeof(serv_or_port)); if (getaddrinfo(host, serv_or_port, &hints, &aitop) != 0) { (void) snprintf(Errmsg, ERRMSGSIZ, gettext("%s: unknown host"), host); *fd = -1; return(0); } for (ai = aitop; ai; ai = ai->ai_next) { if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) continue; s = socket(ai->ai_family, SOCK_STREAM, 0); if (s < 0) { which_err = SOCKET; retval = errno; continue; } if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0 && errno != EINPROGRESS) { which_err = CONNECT; retval = errno; close(s); continue; /* fail -- try next */ } break; /* success */ } if (ai == NULL) { switch (which_err) { case SOCKET: (void) snprintf(Errmsg, ERRMSGSIZ, gettext("in call to socket")); break; case CONNECT: (void) snprintf(Errmsg, ERRMSGSIZ, gettext("in call to connect")); break; default : retval = -1; /* generic error */ (void) snprintf(Errmsg, ERRMSGSIZ, gettext("could not setup network")); break; } if (aitop != NULL) freeaddrinfo(aitop); return(retval); } *fd = s; /* * Set receiver_addr and sender_addr. */ if (cvtkaddr((struct sockaddr_storage *)ai->ai_addr, &receiver_addr) == NULL) { retval = errno; com_err(progname, errno, gettext("while converting socket address")); if (aitop != NULL) freeaddrinfo(aitop); return(retval); } if (aitop != NULL) freeaddrinfo(aitop); socket_length = sizeof(ss); if (getsockname(s, (struct sockaddr *)&ss, &socket_length) < 0) { retval = errno; close(s); (void) snprintf(Errmsg, ERRMSGSIZ, gettext("in call to getsockname")); return(retval); } if (cvtkaddr(&ss, &sender_addr) == NULL) { retval = errno; com_err(progname, errno, gettext("while converting socket address")); return(retval); } return(0); } void kerberos_authenticate(context, auth_context, fd, me, new_creds) krb5_context context; krb5_auth_context *auth_context; int fd; krb5_principal me; krb5_creds ** new_creds; { krb5_error_code retval; krb5_error *error = NULL; krb5_ap_rep_enc_part *rep_result; retval = krb5_auth_con_init(context, auth_context); if (retval) exit(1); krb5_auth_con_setflags(context, *auth_context, KRB5_AUTH_CONTEXT_DO_SEQUENCE); retval = krb5_auth_con_setaddrs(context, *auth_context, &sender_addr, &receiver_addr); if (retval) { com_err(progname, retval, gettext("in krb5_auth_con_setaddrs")); exit(1); } retval = krb5_sendauth(context, auth_context, (void *)&fd, kprop_version, me, creds.server, AP_OPTS_MUTUAL_REQUIRED, NULL, &creds, NULL, &error, &rep_result, new_creds); if (retval) { com_err(progname, retval, gettext("while authenticating to server")); if (error) { if (error->error == KRB_ERR_GENERIC) { if (error->text.data) fprintf(stderr, gettext("Generic remote error: %s\n"), error->text.data); } else if (error->error) { com_err(progname, (krb5_error_code) error->error + ERROR_TABLE_BASE_krb5, gettext("signalled from server")); if (error->text.data) fprintf(stderr, gettext("Error text from server: %s\n"), error->text.data); } krb5_free_error(context, error); } exit(1); } krb5_free_ap_rep_enc_part(context, rep_result); } char * dbpathname; /* * Open the Kerberos database dump file. Takes care of locking it * and making sure that the .ok file is more recent that the database * dump file itself. * * Returns the file descriptor of the database dump file. Also fills * in the size of the database file. */ int open_database(context, data_fn, size) krb5_context context; char *data_fn; int *size; { int fd; int err; struct stat stbuf, stbuf_ok; char *data_ok_fn; static char ok[] = ".dump_ok"; dbpathname = strdup(data_fn); if (!dbpathname) { com_err(progname, ENOMEM, gettext("allocating database file name '%s'"), data_fn); exit(1); } if ((fd = open(dbpathname, O_RDONLY)) < 0) { com_err(progname, errno, gettext("while trying to open %s"), dbpathname); exit(1); } err = krb5_lock_file(context, fd, KRB5_LOCKMODE_SHARED|KRB5_LOCKMODE_DONTBLOCK); if (err == EAGAIN || err == EWOULDBLOCK || errno == EACCES) { com_err(progname, 0, gettext("database locked")); exit(1); } else if (err) { com_err(progname, err, gettext("while trying to lock '%s'"), dbpathname); exit(1); } if (fstat(fd, &stbuf)) { com_err(progname, errno, gettext("while trying to stat %s"), data_fn); exit(1); } if ((data_ok_fn = (char *) malloc(strlen(data_fn)+strlen(ok)+1)) == NULL) { com_err(progname, ENOMEM, gettext("while trying to malloc data_ok_fn")); exit(1); } strcpy(data_ok_fn, data_fn); strcat(data_ok_fn, ok); if (stat(data_ok_fn, &stbuf_ok)) { com_err(progname, errno, gettext("while trying to stat %s"), data_ok_fn); free(data_ok_fn); exit(1); } free(data_ok_fn); if (stbuf.st_mtime > stbuf_ok.st_mtime) { com_err(progname, 0, gettext("'%s' more recent than '%s'."), data_fn, data_ok_fn); exit(1); } *size = stbuf.st_size; return(fd); } void close_database(context, fd) krb5_context context; int fd; { int err; err = krb5_lock_file(context, fd, KRB5_LOCKMODE_UNLOCK); if (err) com_err(progname, err, gettext("while unlocking database '%s'"), dbpathname); free(dbpathname); (void)close(fd); return; } /* * Now we send over the database. We use the following protocol: * Send over a KRB_SAFE message with the size. Then we send over the * database in blocks of KPROP_BLKSIZE, encrypted using KRB_PRIV. * Then we expect to see a KRB_SAFE message with the size sent back. * * At any point in the protocol, we may send a KRB_ERROR message; this * will abort the entire operation. */ void xmit_database(context, auth_context, my_creds, fd, database_fd, in_database_size) krb5_context context; krb5_auth_context auth_context; krb5_creds *my_creds; int fd; int database_fd; int in_database_size; { krb5_int32 sent_size, n; krb5_data inbuf, outbuf; char buf[KPROP_BUFSIZ]; krb5_error_code retval; krb5_error *error; /* These must be 4 bytes */ krb5_ui_4 database_size = in_database_size; krb5_ui_4 send_size; /* * Send over the size */ send_size = htonl(database_size); inbuf.data = (char *) &send_size; inbuf.length = sizeof(send_size); /* must be 4, really */ /* KPROP_CKSUMTYPE */ retval = krb5_mk_safe(context, auth_context, &inbuf, &outbuf, NULL); if (retval) { com_err(progname, retval, gettext("while encoding database size")); send_error(context, my_creds, fd, gettext("while encoding database size"), retval); exit(1); } retval = krb5_write_message(context, (void *) &fd, &outbuf); if (retval) { krb5_free_data_contents(context, &outbuf); com_err(progname, retval, gettext("while sending database size")); exit(1); } krb5_free_data_contents(context, &outbuf); /* * Initialize the initial vector. */ retval = krb5_auth_con_initivector(context, auth_context); if (retval) { send_error(context, my_creds, fd, gettext("failed while initializing i_vector"), retval); com_err(progname, retval, gettext("while allocating i_vector")); exit(1); } /* * Send over the file, block by block.... */ inbuf.data = buf; sent_size = 0; while ((n = read(database_fd, buf, sizeof(buf)))) { inbuf.length = n; retval = krb5_mk_priv(context, auth_context, &inbuf, &outbuf, NULL); if (retval) { snprintf(buf, sizeof (buf), gettext("while encoding database block starting at %d"), sent_size); com_err(progname, retval, buf); send_error(context, my_creds, fd, buf, retval); exit(1); } retval = krb5_write_message(context, (void *)&fd,&outbuf); if (retval) { krb5_free_data_contents(context, &outbuf); com_err(progname, retval, gettext("while sending database block starting at %d"), sent_size); exit(1); } krb5_free_data_contents(context, &outbuf); sent_size += n; if (debug) printf(gettext("%d bytes sent.\n"), sent_size); } if (sent_size != database_size) { com_err(progname, 0, gettext("Premature EOF found for database file!")); send_error(context, my_creds, fd,gettext("Premature EOF found for database file!"), KRB5KRB_ERR_GENERIC); exit(1); } /* * OK, we've sent the database; now let's wait for a success * indication from the remote end. */ retval = krb5_read_message(context, (void *) &fd, &inbuf); if (retval) { com_err(progname, retval, gettext("while reading response from server")); exit(1); } /* * If we got an error response back from the server, display * the error message */ if (krb5_is_krb_error(&inbuf)) { retval = krb5_rd_error(context, &inbuf, &error); if (retval) { com_err(progname, retval, gettext("while decoding error response from server")); exit(1); } if (error->error == KRB_ERR_GENERIC) { if (error->text.data) fprintf(stderr, gettext("Generic remote error: %s\n"), error->text.data); } else if (error->error) { com_err(progname, (krb5_error_code) error->error + ERROR_TABLE_BASE_krb5, gettext("signalled from server")); if (error->text.data) fprintf(stderr, gettext("Error text from server: %s\n"), error->text.data); } krb5_free_error(context, error); exit(1); } retval = krb5_rd_safe(context,auth_context,&inbuf,&outbuf,NULL); if (retval) { com_err(progname, retval, gettext("while decoding final size packet from server")); exit(1); } memcpy((char *)&send_size, outbuf.data, sizeof(send_size)); send_size = ntohl(send_size); if (send_size != database_size) { com_err(progname, 0, gettext("Kpropd sent database size %d, expecting %d"), send_size, database_size); exit(1); } free(outbuf.data); free(inbuf.data); } void send_error(context, my_creds, fd, err_text, err_code) krb5_context context; krb5_creds *my_creds; int fd; char *err_text; krb5_error_code err_code; { krb5_error error; const char *text; krb5_data outbuf; memset((char *)&error, 0, sizeof(error)); krb5_us_timeofday(context, &error.ctime, &error.cusec); error.server = my_creds->server; error.client = my_principal; error.error = err_code - ERROR_TABLE_BASE_krb5; if (error.error > 127) error.error = KRB_ERR_GENERIC; if (err_text) text = err_text; else text = error_message(err_code); error.text.length = strlen(text) + 1; error.text.data = malloc((unsigned int) error.text.length); if (error.text.data) { strcpy(error.text.data, text); if (!krb5_mk_error(context, &error, &outbuf)) { (void) krb5_write_message(context, (void *)&fd,&outbuf); krb5_free_data_contents(context, &outbuf); } free(error.text.data); } } void update_last_prop_file(hostname, file_name) char *hostname; char *file_name; { /* handle slave locking/failure stuff */ char *file_last_prop; int fd; static char last_prop[]=".last_prop"; if ((file_last_prop = (char *)malloc(strlen(file_name) + strlen(hostname) + 1 + strlen(last_prop) + 1)) == NULL) { com_err(progname, ENOMEM, gettext("while allocating filename for update_last_prop_file")); return; } strcpy(file_last_prop, file_name); /* * If a nondefault file name was specified then we should not add an * extraneous host name to the file name given that a file name could * have already specified a host name and therefore would be redundant. */ if (strcmp(file_name, KPROP_DEFAULT_FILE) == 0) { strcat(file_last_prop, "."); strcat(file_last_prop, hostname); } strcat(file_last_prop, last_prop); if ((fd = THREEPARAMOPEN(file_last_prop, O_WRONLY|O_CREAT|O_TRUNC, 0600)) < 0) { com_err(progname, errno, gettext("while creating 'last_prop' file, '%s'"), file_last_prop); free(file_last_prop); return; } write(fd, "", 1); free(file_last_prop); close(fd); return; } /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _KPROP_H #define _KPROP_H #ifdef __cplusplus extern "C" { #endif /* * slave/kprop.h * * 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. * * */ #define KPROP_SERVICE_NAME "host" #define TGT_SERVICE_NAME "krbtgt" #define KPROP_SERVICE "krb5_prop" #define KPROP_PROT_VERSION "kprop5_01" #define KPROP_BUFSIZ 32768 extern krb5_address *cvtkaddr(struct sockaddr_storage *ss, krb5_address *krbap); /* pathnames are in osconf.h, included via k5-int.h */ #ifdef __cplusplus } #endif #endif /* !_KPROP_H */ #!/bin/sh # # Copyright (c) 2000 by Sun Microsystems, Inc. # All rights reserved. # if [ $# -lt 1 ] then exit 0 fi /usr/sbin/kdb5_util dump /var/krb5/slave_datatrans for kdc in $* do /usr/lib/krb5/kprop -f /var/krb5/slave_datatrans $kdc done /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * 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. */ /* * slave/kpropd.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. * * * XXX We need to modify the protocol so that an acknowledge is set * after each block, instead after the entire series is sent over. * The reason for this is so that error packets can get interpreted * right away. If you don't do this, the sender may never get the * error packet, because it will die an EPIPE trying to complete the * write... */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "com_err.h" #include #include "kprop.h" #include #include "iprop.h" #include #include /* Solaris Kerberos */ #include #define SYSLOG_CLASS LOG_DAEMON #define INITIAL_TIMER 10 char *poll_time = NULL; char *def_realm = NULL; boolean_t runonce = B_FALSE; /* * Global fd to close upon alarm time-out. */ volatile int gfd = -1; /* * This struct simulates the use of _kadm5_server_handle_t */ typedef struct _kadm5_iprop_handle_t { krb5_ui_4 magic_number; krb5_ui_4 struct_version; krb5_ui_4 api_version; char *cache_name; int destroy_cache; CLIENT *clnt; krb5_context context; kadm5_config_params params; struct _kadm5_iprop_handle_t *lhandle; } *kadm5_iprop_handle_t; static char *kprop_version = KPROP_PROT_VERSION; char *progname; int debug = 0; char *srvtab = 0; int standalone = 0; krb5_principal server; /* This is our server principal name */ krb5_principal client; /* This is who we're talking to */ krb5_context kpropd_context; krb5_auth_context auth_context; char *realm = NULL; /* Our realm */ char *file = KPROPD_DEFAULT_FILE; char *temp_file_name; char *kdb5_util = KPROPD_DEFAULT_KDB5_UTIL; char *kerb_database = NULL; char *acl_file_name = KPROPD_ACL_FILE; krb5_address sender_addr; krb5_address receiver_addr; short port = 0; void PRS (int, char**); int do_standalone (iprop_role iproprole); void doit (int); krb5_error_code do_iprop(kdb_log_context *log_ctx); /* Solaris Kerberos */ void kerberos_authenticate (krb5_context, int, krb5_principal *, krb5_enctype *, struct sockaddr_storage *); krb5_boolean authorized_principal (krb5_context, krb5_principal, krb5_enctype); void recv_database (krb5_context, int, int, krb5_data *); void load_database (krb5_context, char *, char *); void send_error (krb5_context, int, krb5_error_code, char *); void recv_error (krb5_context, krb5_data *); int convert_polltime (char *); unsigned int backoff_from_master (int *); static void usage() { fprintf(stderr, gettext("\nUsage: %s\n"), /* progname may be a long pathname */ progname); fprintf(stderr, gettext("\t[-r realm] [-s srvtab] [-dS] [-f slave_file]\n")); fprintf(stderr, gettext("\t[-F kerberos_db_file ] [-p kdb5_util_pathname]\n")); fprintf(stderr, gettext("\t[-P port] [-a acl_file]\n")); exit(1); } int main(argc, argv) int argc; char **argv; { krb5_error_code retval; int ret = 0; kdb_log_context *log_ctx; int iprop_supported; krb5_boolean is_master = FALSE; PRS(argc, argv); log_ctx = kpropd_context->kdblog_context; if (log_ctx && (log_ctx->iproprole == IPROP_SLAVE)) { /* * We wanna do iprop ! */ retval = krb5_db_supports_iprop(kpropd_context, &iprop_supported); if (retval) { /* Solaris Kerberos: Keep error messages consistent */ com_err(progname, retval, gettext("while determining if dbmodule plugin " "supports iprop")); exit(1); } if (!iprop_supported) { /* Solaris Kerberos: Keep error messages consistent */ com_err(progname, 0, gettext("Current dbmodule plugin does not support " "iprop")); exit(1); } /* * Solaris Kerberos: * Ensure that kpropd is only run on a slave */ if (retval = kadm5_is_master(kpropd_context, def_realm, &is_master)) { com_err(progname, retval, gettext("while trying to determine whether host is " "master KDC for realm %s"), def_realm); exit(1); } if (is_master == TRUE) { char *master = NULL; kadm5_get_master(kpropd_context, def_realm, &master); com_err(progname, 0, gettext("%s is the master KDC for the realm %s. " "%s can only be run on a slave KDC"), master ? master : "unknown", def_realm, progname); exit(1); } retval = do_iprop(log_ctx); if (retval) { /* Solaris Kerberos: Keep error messages consistent */ com_err(progname, retval, gettext("while doing iprop")); exit(1); } } else { /* * Solaris Kerberos: * Ensure that the kpropd.acl file exists and contains at least * 1 entry. */ FILE *tmp_acl_file; int seen_file = 0; char buf[1024]; tmp_acl_file = fopen(acl_file_name, "r"); if (!tmp_acl_file) { com_err(progname, errno, gettext("while opening acl file %s"), acl_file_name); exit(1); } while (!feof(tmp_acl_file) && !seen_file ) { if (!fgets(buf, sizeof(buf), tmp_acl_file)) break; if (buf[0] != '#' && !isspace(buf[0])) seen_file = 1; } if (!seen_file) { com_err(progname, 0, gettext("No entries found in %s. Can't " "authorize propagation requests"), acl_file_name); exit(1); } fclose(tmp_acl_file); if (standalone) ret = do_standalone(IPROP_NULL); else doit(0); } exit(ret); } void resync_alarm(int sn) { close(gfd); if (debug) fprintf(stderr, gettext("resync_alarm: closing fd: %d\n"), gfd); gfd = -1; } int do_standalone(iprop_role iproprole) { struct linger linger; struct servent *sp; int finet, fromlen, s; int on = 1; int ret, status = 0; struct sockaddr_in6 sin6 = { AF_INET6 }; int sin6_size = sizeof (sin6); /* * Timer for accept/read calls, in case of network type errors. */ int backoff_timer = INITIAL_TIMER; retry: /* listen for either ipv4 or ipv6 */ finet = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); if (finet < 0 ) { com_err(progname, errno, gettext("while obtaining socket")); exit(1); } if(!port) { sp = getservbyname(KPROP_SERVICE, "tcp"); if (sp == NULL) { com_err(progname, 0, gettext("%s/tcp: unknown service"), KPROP_SERVICE); exit(1); } sin6.sin6_port = sp->s_port; } else sin6.sin6_port = port; /* * We need to close the socket immediately if iprop is enabled, * since back-to-back full resyncs are possible, so we do not * linger around for too long */ if (iproprole == IPROP_SLAVE) { if (setsockopt(finet, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0) com_err(progname, errno, gettext("while setting socket option (SO_REUSEADDR)")); linger.l_onoff = 1; linger.l_linger = 2; if (setsockopt(finet, SOL_SOCKET, SO_LINGER, (void *)&linger, sizeof(linger)) < 0) com_err(progname, errno, gettext("while setting socket option (SO_LINGER)")); /* * We also want to set a timer so that the slave is not waiting * until infinity for an update from the master. */ gfd = finet; signal(SIGALRM, resync_alarm); if (debug) { fprintf(stderr, "do_standalone: setting resync alarm to %d\n", backoff_timer); } if (alarm(backoff_timer) != 0) { if (debug) { fprintf(stderr, gettext("%s: alarm already set\n"), progname); } } backoff_timer *= 2; } if ((ret = bind(finet, (struct sockaddr *)&sin6, sizeof(sin6))) < 0) { if (debug) { on = 1; fprintf(stderr, gettext("%s: attempting to rebind socket " "with SO_REUSEADDR\n"), progname); if (setsockopt(finet, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0) { com_err(progname, errno, gettext("while setting socket option (SO_REUSEADDR)")); } ret = bind(finet, (struct sockaddr *) &sin6, sizeof(sin6)); } if (ret < 0) { /* * Solaris Kerberos: * com_err will print the err msg associated with errno */ #if 0 perror(gettext("bind")); #endif com_err(progname, errno, gettext("while binding listener socket")); exit(1); } } if (!debug && (iproprole != IPROP_SLAVE)) { /* Solaris Kerberos: Indicate where further messages will be sent */ fprintf(stderr, gettext("%s: Logging to SYSLOG with LOG_DAEMON facility\n"), progname); if (daemon(1, 0)) { com_err(progname, errno, gettext("while daemonizing")); exit(1); } rem_default_com_err_hook(); } #ifdef PID_FILE if ((pidfile = fopen(PID_FILE, "w")) != NULL) { fprintf(pidfile, gettext("%d\n"), getpid()); fclose(pidfile); } else com_err(progname, errno, gettext("while opening pid file %s for writing"), PID_FILE); #endif if (listen(finet, 5) < 0) { /* Solaris Kerberos: Keep error messages consistent */ com_err(progname, errno, gettext("while listening on socket")); exit(1); } while (1) { int child_pid; s = accept(finet, (struct sockaddr *) &sin6, &sin6_size); if (s < 0) { int e = errno; if (e != EINTR) { /* * Solaris Kerberos: Keep error messages * consistent */ com_err(progname, e, gettext("while accepting connection")); backoff_timer = INITIAL_TIMER; } /* * If we got EBADF, an alarm signal handler closed * the file descriptor on us. */ if (e != EBADF) close(finet); /* * An alarm could have been set and the fd closed, we * should retry in case of transient network error for * up to a couple of minutes. */ if (backoff_timer > 120) return (EINTR); goto retry; } alarm(0); gfd = -1; if (debug && (iproprole != IPROP_SLAVE)) child_pid = 0; else child_pid = fork(); switch (child_pid) { case -1: com_err(progname, errno, gettext("while forking")); exit(1); /*NOTREACHED*/ case 0: /* child */ (void) close(finet); doit(s); close(s); _exit(0); /*NOTREACHED*/ default: /* parent */ /* * Errors should not be considered fatal in the iprop case as we * could have transient type errors, such as network outage, etc. * Sleeping 3s for 2s linger interval. */ if (wait(&status) < 0) { com_err(progname, errno, gettext("while waiting to receive database")); if (iproprole != IPROP_SLAVE) exit(1); sleep(3); } close(s); if (iproprole == IPROP_SLAVE) close(finet); if ((ret = WEXITSTATUS(status)) != 0) return (ret); } if (iproprole == IPROP_SLAVE) break; } return (0); } void doit(fd) int fd; { struct sockaddr_storage from; socklen_t fromlen; int on = 1; struct hostent *hp; krb5_error_code retval; krb5_data confmsg; int lock_fd; mode_t omask; krb5_enctype etype; int database_fd; char ntop[NI_MAXHOST] = ""; krb5_context doit_context; kdb_log_context *log_ctx; retval = krb5_init_context(&doit_context); if (retval) { com_err(progname, retval, gettext("while initializing krb5")); exit(1); } log_ctx = kpropd_context->kdblog_context; if (log_ctx && (log_ctx->iproprole == IPROP_SLAVE)) { ulog_set_role(doit_context, IPROP_SLAVE); /* * We also want to set a timer so that the slave is not waiting * until infinity for an update from the master. */ if (debug) fprintf(stderr, "doit: setting resync alarm to %ds\n", INITIAL_TIMER); signal(SIGALRM, resync_alarm); gfd = fd; if (alarm(INITIAL_TIMER) != 0) { if (debug) { fprintf(stderr, gettext("%s: alarm already set\n"), progname); } } } fromlen = (socklen_t)sizeof (from); if (getpeername(fd, (struct sockaddr *) &from, &fromlen) < 0) { fprintf(stderr, "%s: ", progname); perror(gettext("getpeername")); exit(1); } if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (caddr_t) &on, sizeof (on)) < 0) { com_err(progname, errno, gettext("while attempting setsockopt (SO_KEEPALIVE)")); } if (getnameinfo((struct sockaddr *)&from, fromlen, ntop, sizeof(ntop), NULL, 0, NI_NUMERICHOST) != 0) { /* getnameifo failed so use inet_ntop() to get printable addresses */ if (from.ss_family == AF_INET) { inet_ntop(AF_INET, (const void *)&ss2sin(&from)->sin_addr, ntop, sizeof(ntop)); } else if (from.ss_family == AF_INET6 && ! IN6_IS_ADDR_V4MAPPED(&ss2sin6(&from)->sin6_addr)) { ipaddr_t v4addr; inet_ntop(AF_INET6, (const void *)&ss2sin6(&from)->sin6_addr, ntop, sizeof(ntop)); } /* ipv4 mapped ipv6 addrs handled later */ } if (from.ss_family == AF_INET || from.ss_family == AF_INET6) { if (from.ss_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&ss2sin6(&from)->sin6_addr)) { ipaddr_t v4addr; /* coerce ipv4 mapped ipv6 addr to normal ipv4 addr */ IN6_V4MAPPED_TO_IPADDR(&(ss2sin6(&from)->sin6_addr), v4addr); inet_ntop(AF_INET, (const void *) &v4addr, ntop, sizeof(ntop)); } syslog(LOG_INFO, gettext("Connection from %s"), ntop); if (debug) printf("Connection from %s\n", ntop); } else { /* address family isn't either AF_INET || AF_INET6 */ syslog(LOG_INFO, gettext("Connection from unknown address family:%d"), from.ss_family); if (debug) { printf(gettext("Connection from unknown address family:%d"), from.ss_family); } } /* * Now do the authentication */ /* Solaris Kerberos */ kerberos_authenticate(doit_context, fd, &client, &etype, &from); /* * Turn off alarm upon successful authentication from master. */ alarm(0); gfd = -1; if (!authorized_principal(doit_context, client, etype)) { char *name; retval = krb5_unparse_name(doit_context, client, &name); if (retval) { /* Solaris Kerberos: Keep error messages consistent */ com_err(progname, retval, gettext("while unparsing client name")); exit(1); } syslog(LOG_WARNING, gettext("Rejected connection from unauthorized principal %s"), name); free(name); exit(1); } omask = umask(077); lock_fd = open(temp_file_name, O_RDWR|O_CREAT, 0600); (void) umask(omask); retval = krb5_lock_file(doit_context, lock_fd, KRB5_LOCKMODE_EXCLUSIVE|KRB5_LOCKMODE_DONTBLOCK); if (retval) { com_err(progname, retval, gettext("while trying to lock '%s'"), temp_file_name); exit(1); } if ((database_fd = open(temp_file_name, O_WRONLY|O_CREAT|O_TRUNC, 0600)) < 0) { com_err(progname, errno, gettext("while opening database file, '%s'"), temp_file_name); exit(1); } recv_database(doit_context, fd, database_fd, &confmsg); if (rename(temp_file_name, file)) { /* Solaris Kerberos: Keep error messages consistent */ com_err(progname, errno, gettext("while renaming %s to %s"), temp_file_name, file); exit(1); } retval = krb5_lock_file(doit_context, lock_fd, KRB5_LOCKMODE_SHARED); if (retval) { com_err(progname, retval, gettext("while downgrading lock on '%s'"), temp_file_name); exit(1); } load_database(doit_context, kdb5_util, file); retval = krb5_lock_file(doit_context, lock_fd, KRB5_LOCKMODE_UNLOCK); if (retval) { com_err(progname, retval, gettext("while unlocking '%s'"), temp_file_name); exit(1); } (void)close(lock_fd); /* * Send the acknowledgement message generated in * recv_database, then close the socket. */ retval = krb5_write_message(doit_context, (void *) &fd, &confmsg); if (retval) { krb5_free_data_contents(doit_context, &confmsg); com_err(progname, retval, gettext("while sending # of received bytes")); exit(1); } krb5_free_data_contents(doit_context, &confmsg); if (close(fd) < 0) { com_err(progname, errno, gettext("while trying to close database file")); exit(1); } exit(0); } /* * Routine to handle incremental update transfer(s) from master KDC */ krb5_error_code do_iprop(kdb_log_context *log_ctx) { CLIENT *cl; kadm5_ret_t retval; kadm5_config_params params; krb5_ccache cc; krb5_principal iprop_svc_principal; void *server_handle = NULL; char *iprop_svc_princstr = NULL; char *master_svc_princstr = NULL; char *admin_server = NULL; char *keytab_name = NULL; unsigned int pollin, backoff_time; int backoff_cnt = 0; int reinit_cnt = 0; int ret; boolean_t frdone = B_FALSE; kdb_incr_result_t *incr_ret; static kdb_last_t mylast; kdb_fullresync_result_t *full_ret; char *full_resync_arg = NULL; kadm5_iprop_handle_t handle; kdb_hlog_t *ulog; krb5_keytab kt; krb5_keytab_entry entry; char kt_name[MAX_KEYTAB_NAME_LEN]; /* * Solaris Kerberos: * Delay daemonizing until some basic configuration checks have been * performed */ #if 0 if (!debug) daemon(0, 0); #endif pollin = (unsigned int)0; (void) memset((char *)¶ms, 0, sizeof (params)); ulog = log_ctx->ulog; params.mask |= KADM5_CONFIG_REALM; params.realm = def_realm; if (master_svc_princstr == NULL) { if (retval = kadm5_get_kiprop_host_srv_name(kpropd_context, def_realm, &master_svc_princstr)) { /* Solaris Kerberos: keep error messages consistent */ com_err(progname, retval, gettext("while getting kiprop host based " "service name for realm %s"), def_realm); exit(1); } } /* * Set cc to the default credentials cache */ if (retval = krb5_cc_default(kpropd_context, &cc)) { com_err(progname, retval, gettext("while opening default " "credentials cache")); exit(1); } retval = krb5_sname_to_principal(kpropd_context, NULL, KIPROP_SVC_NAME, KRB5_NT_SRV_HST, &iprop_svc_principal); if (retval) { com_err(progname, retval, gettext("while trying to construct " "host service principal")); exit(1); } /* Solaris Kerberos */ if (krb5_is_referral_realm(krb5_princ_realm(kpropd_context, iprop_svc_principal))) { krb5_data *r = krb5_princ_realm(kpropd_context, iprop_svc_principal); assert(def_realm != NULL); r->length = strlen(def_realm); r->data = strdup(def_realm); if (r->data == NULL) { com_err(progname, retval, ("while determining local service principal name")); exit(1); } } if (retval = krb5_unparse_name(kpropd_context, iprop_svc_principal, &iprop_svc_princstr)) { com_err(progname, retval, gettext("while canonicalizing " "principal name")); krb5_free_principal(kpropd_context, iprop_svc_principal); exit(1); } /* * Solaris Kerberos: * Check to see if kiprop/@REALM is in the keytab */ kt_name[0] = '\0'; if (retval = krb5_kt_default_name(kpropd_context, kt_name, MAX_KEYTAB_NAME_LEN)){ com_err(progname, retval, gettext ("while resolving the " "name of the default keytab")); } if (retval = krb5_kt_default(kpropd_context, &kt)) { com_err(progname, retval, gettext ("while resolving default " "keytab")); krb5_free_principal(kpropd_context, iprop_svc_principal); exit(1); } if (retval = krb5_kt_get_entry(kpropd_context, kt, iprop_svc_principal, 0, 0, &entry)) { com_err(progname, retval, gettext("while retrieving entry %s " "from %s"), iprop_svc_princstr, kt_name[0] ? kt_name : "default keytab"); krb5_kt_close(kpropd_context,kt); krb5_free_principal(kpropd_context, iprop_svc_principal); exit(1); } krb5_kt_close(kpropd_context,kt); krb5_free_principal(kpropd_context, iprop_svc_principal); if (!debug) { /* Solaris Kerberos: Indicate where further messages will be sent */ fprintf(stderr, gettext("%s: Logging to SYSLOG\n"), progname); if (daemon(0, 0)) { com_err(progname, errno, gettext("while daemonizing")); exit(1); } rem_default_com_err_hook(); } reinit: /* * Authentication, initialize rpcsec_gss handle etc. */ retval = kadm5_init_with_skey(iprop_svc_princstr, keytab_name, master_svc_princstr, ¶ms, KADM5_STRUCT_VERSION, KADM5_API_VERSION_2, NULL, &server_handle); if (retval) { if (retval == KADM5_RPC_ERROR) { reinit_cnt++; if (server_handle) kadm5_destroy((void *) server_handle); server_handle = (void *)NULL; handle = (kadm5_iprop_handle_t)NULL; com_err(progname, retval, gettext( "while attempting to connect" " to master KDC ... retrying")); backoff_time = backoff_from_master(&reinit_cnt); (void) sleep(backoff_time); goto reinit; } else { /* Solaris Kerberos: Be more verbose */ com_err(progname, retval, gettext("while initializing %s interface for " "%s"), progname, iprop_svc_princstr); if (retval == KADM5_BAD_CLIENT_PARAMS || retval == KADM5_BAD_SERVER_PARAMS) usage(); exit(1); } } /* * Reset re-initialization count to zero now. */ reinit_cnt = backoff_time = 0; /* * Reset the handle to the correct type for the RPC call */ handle = server_handle; /* * If we have reached this far, we have succesfully established * a RPCSEC_GSS connection; we now start polling for updates */ if (poll_time == NULL) { if ((poll_time = (char *)strdup("2m")) == NULL) { /* Solaris Kerberos: Keep error messages consistent */ com_err(progname, ENOMEM, gettext("while allocating poll_time")); exit(1); } } if (pollin == (unsigned int)0) pollin = convert_polltime(poll_time); for (;;) { incr_ret = NULL; full_ret = NULL; /* * Get the most recent ulog entry sno + ts, which * we package in the request to the master KDC */ mylast.last_sno = ulog->kdb_last_sno; mylast.last_time = ulog->kdb_last_time; /* * Loop continuously on an iprop_get_updates_1(), * so that we can keep probing the master for updates * or (if needed) do a full resync of the krb5 db. */ incr_ret = iprop_get_updates_1(&mylast, handle->clnt); if (incr_ret == (kdb_incr_result_t *)NULL) { clnt_perror(handle->clnt, "iprop_get_updates call failed"); if (server_handle) kadm5_destroy((void *)server_handle); server_handle = (void *)NULL; handle = (kadm5_iprop_handle_t)NULL; goto reinit; } switch (incr_ret->ret) { case UPDATE_FULL_RESYNC_NEEDED: /* * We dont do a full resync again, if the last * X'fer was a resync and if the master sno is * still "0", i.e. no updates so far. */ if ((frdone == B_TRUE) && (incr_ret->lastentry.last_sno == 0)) { break; } else { full_ret = iprop_full_resync_1((void *) &full_resync_arg, handle->clnt); if (full_ret == (kdb_fullresync_result_t *) NULL) { clnt_perror(handle->clnt, "iprop_full_resync call failed"); if (server_handle) kadm5_destroy((void *) server_handle); server_handle = (void *)NULL; handle = (kadm5_iprop_handle_t)NULL; goto reinit; } } switch (full_ret->ret) { case UPDATE_OK: backoff_cnt = 0; /* * We now listen on the kprop port for * the full dump */ ret = do_standalone(log_ctx->iproprole); if (debug) if (ret) fprintf(stderr, gettext("Full resync " "was unsuccessful\n")); else fprintf(stderr, gettext("Full resync " "was successful\n")); if (ret) { syslog(LOG_WARNING, gettext("kpropd: Full resync, " "invalid return.")); /* * Start backing-off immediately after * failure. */ backoff_cnt++; frdone = B_FALSE; } else frdone = B_TRUE; break; case UPDATE_BUSY: /* * Exponential backoff */ backoff_cnt++; break; case UPDATE_FULL_RESYNC_NEEDED: case UPDATE_NIL: default: backoff_cnt = 0; frdone = B_FALSE; syslog(LOG_ERR, gettext("kpropd: Full resync," " invalid return from master KDC.")); break; case UPDATE_PERM_DENIED: syslog(LOG_ERR, gettext("kpropd: Full resync," " permission denied.")); goto error; case UPDATE_ERROR: syslog(LOG_ERR, gettext("kpropd: Full resync," " error returned from master KDC.")); goto error; } break; case UPDATE_OK: backoff_cnt = 0; frdone = B_FALSE; /* * ulog_replay() will convert the ulog updates to db * entries using the kdb conv api and will commit * the entries to the slave kdc database */ retval = ulog_replay(kpropd_context, incr_ret); if (retval) { syslog(LOG_ERR, gettext("kpropd: ulog_replay" " failed, updates not registered.")); break; } if (debug) fprintf(stderr, gettext("Update transfer " "from master was OK\n")); break; case UPDATE_PERM_DENIED: syslog(LOG_ERR, gettext("kpropd: get_updates," " permission denied.")); goto error; case UPDATE_ERROR: syslog(LOG_ERR, gettext("kpropd: get_updates, error " "returned from master KDC.")); goto error; case UPDATE_BUSY: /* * Exponential backoff */ backoff_cnt++; break; case UPDATE_NIL: /* * Master-slave are in sync */ if (debug) fprintf(stderr, gettext("Master, slave KDC's " "are in-sync, no updates\n")); backoff_cnt = 0; frdone = B_FALSE; break; default: backoff_cnt = 0; syslog(LOG_ERR, gettext("kpropd: get_updates," " invalid return from master KDC.")); break; } if (runonce == B_TRUE) goto done; /* * Sleep for the specified poll interval (Default is 2 mts), * or do a binary exponential backoff if we get an * UPDATE_BUSY signal */ if (backoff_cnt > 0) { backoff_time = backoff_from_master(&backoff_cnt); if (debug) fprintf(stderr, gettext("Busy signal received " "from master, backoff for %d secs\n"), backoff_time); (void) sleep(backoff_time); } else (void) sleep(pollin); } error: if (debug) fprintf(stderr, gettext("ERROR returned by master, bailing\n")); syslog(LOG_ERR, gettext("kpropd: ERROR returned by master KDC," " bailing.\n")); done: if (poll_time) free(poll_time); if(iprop_svc_princstr) free(iprop_svc_princstr); if (master_svc_princstr) free(master_svc_princstr); if (retval = krb5_cc_close(kpropd_context, cc)) { com_err(progname, retval, gettext("while closing default ccache")); exit(1); } if (def_realm) free(def_realm); if (server_handle) kadm5_destroy((void *)server_handle); if (kpropd_context) krb5_free_context(kpropd_context); if (runonce == B_TRUE) return (0); else exit(1); } /* * Do exponential backoff, since master KDC is BUSY or down */ unsigned int backoff_from_master(int *cnt) { unsigned int btime; btime = (unsigned int)(2<<(*cnt)); if (btime > MAX_BACKOFF) { btime = MAX_BACKOFF; *cnt--; } return (btime); } /* * Routine to convert the `pollstr' string to seconds */ int convert_polltime(char *pollstr) { char *tokenptr = NULL; int len, polltime; len = polltime = 0; if ((len = strcspn(pollstr, "s")) < strlen(pollstr)) { tokenptr = malloc((len + 1) * sizeof(char)); (void) strlcpy(tokenptr, pollstr, len + 1); polltime = atoi(tokenptr); } if ((len = strcspn(pollstr, "m")) < strlen(pollstr)) { tokenptr = malloc((len + 1) * sizeof(char)); (void) strlcpy(tokenptr, pollstr, len + 1); polltime = atoi(tokenptr) * 60; } if ((len = strcspn(pollstr, "h")) < strlen(pollstr)) { tokenptr = malloc((len + 1) * sizeof(char)); (void) strlcpy(tokenptr, pollstr, len + 1); polltime = atoi(tokenptr) * 3600; } if (tokenptr != NULL) free(tokenptr); /* * If we have a bogus pollstr value, set polltime to the * default of 2 mts (120 seconds). */ if (polltime == 0) polltime = 120; return (polltime); } static void kpropd_com_err_proc(whoami, code, fmt, args) const char *whoami; long code; const char *fmt; va_list args; { char error_buf[8096]; error_buf[0] = '\0'; if (fmt) vsprintf(error_buf, fmt, args); syslog(LOG_ERR, "%s%s%s%s%s", whoami ? whoami : "", whoami ? ": " : "", code ? error_message(code) : "", code ? " " : "", error_buf); } void PRS(argc,argv) int argc; char **argv; { register char *word, ch; char *cp; int c; struct hostent *hp; char my_host_name[MAXHOSTNAMELEN], buf[BUFSIZ]; krb5_error_code retval; static const char tmp[] = ".temp"; kadm5_config_params params; (void) setlocale(LC_ALL, ""); #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ #define TEXT_DOMAIN "KPROPD_TEST" /* Use this only if it weren't */ #endif (void) textdomain(TEXT_DOMAIN); (void) memset((char *) ¶ms, 0, sizeof (params)); retval = krb5_init_context(&kpropd_context); if (retval) { com_err(argv[0], retval, gettext("while initializing krb5")); exit(1); } /* Solaris Kerberos: Sanitize progname */ progname = basename(argv[0]); while ((c = getopt(argc, argv, "dtf:F:p:P:r:s:Sa:")) != EOF){ switch (c) { case 'd': debug++; break; case 't': /* * Undocumented option - for testing only. * * Option to run the kpropd server exactly * once (this is true only if iprop is enabled). */ runonce = B_TRUE; break; case 'f': file = optarg; if (!file) usage(); break; case 'F': kerb_database = optarg; if (!kerb_database) usage(); break; case 'p': kdb5_util = optarg; if (!kdb5_util) usage(); break; case 'P': port = htons(atoi(optarg)); if (!port) usage(); break; case 'r': realm = optarg; if (!realm) usage(); params.realm = realm; params.mask |= KADM5_CONFIG_REALM; break; case 's': srvtab = optarg; if (!srvtab) usage(); break; case 'S': standalone++; break; case 'a': acl_file_name = optarg; if (!acl_file_name) usage(); break; case '?': default: usage(); } } /* * If not in debug mode, switch com_err reporting to syslog */ if (! debug) { openlog("kpropd", LOG_PID | LOG_ODELAY, SYSLOG_CLASS); /* * Solaris Kerberos: * Don't replace default logging. Add a new logging channel. * Stop logging to stderr when daemonizing */ add_com_err_hook(kpropd_com_err_proc); } /* * Get my hostname, so we can construct my service name */ retval = krb5_sname_to_principal(kpropd_context, NULL, KPROP_SERVICE_NAME, KRB5_NT_SRV_HST, &server); if (retval) { /* Solaris Kerberos: Keep error messages consistent */ com_err(progname, retval, gettext("while trying to construct my service name")); exit(1); } if (realm) { retval = krb5_set_principal_realm(kpropd_context, server, realm); if (retval) { com_err(progname, errno, gettext("while constructing my service realm")); exit(1); } } /* * Construct the name of the temporary file. */ if ((temp_file_name = (char *) malloc(strlen(file) + strlen(tmp) + 1)) == NULL) { com_err(progname, ENOMEM, gettext("while allocating filename for temp file")); exit(1); } strcpy(temp_file_name, file); strcat(temp_file_name, tmp); retval = kadm5_get_config_params(kpropd_context, 1, NULL, ¶ms, ¶ms); if (retval) { com_err(progname, retval, gettext("while initializing")); exit(1); } if (params.iprop_enabled == TRUE) { ulog_set_role(kpropd_context, IPROP_SLAVE); poll_time = params.iprop_polltime; if (ulog_map(kpropd_context, ¶ms, FKPROPD)) { /* Solaris Kerberos: Keep error messages consistent */ com_err(progname, errno, gettext("while mapping log")); exit(1); } } /* * Grab the realm info and check if iprop is enabled. */ if (def_realm == NULL) { retval = krb5_get_default_realm(kpropd_context, &def_realm); if (retval) { /* Solaris Kerberos: Keep error messages consistent */ com_err(progname, retval, gettext("while retrieving default realm")); exit(1); } } } /* * Figure out who's calling on the other end of the connection.... */ /* Solaris Kerberos */ void kerberos_authenticate(context, fd, clientp, etype, ss) krb5_context context; int fd; krb5_principal * clientp; krb5_enctype * etype; struct sockaddr_storage * ss; { krb5_error_code retval; krb5_ticket * ticket; struct sockaddr_storage r_ss; int ss_length; krb5_keytab keytab = NULL; /* * Set recv_addr and send_addr */ /* Solaris Kerberos */ if (cvtkaddr(ss, &sender_addr) == NULL) { com_err(progname, errno, gettext("while converting socket address")); exit(1); } ss_length = sizeof (r_ss); if (getsockname(fd, (struct sockaddr *) &r_ss, &ss_length)) { com_err(progname, errno, gettext("while getting local socket address")); exit(1); } if (cvtkaddr(&r_ss, &receiver_addr) == NULL) { com_err(progname, errno, gettext("while converting socket address")); exit(1); } if (debug) { char *name; retval = krb5_unparse_name(context, server, &name); if (retval) { /* Solaris Kerberos: Keep error messages consistent */ com_err(progname, retval, gettext("while unparsing server name")); exit(1); } printf(gettext("krb5_recvauth(%d, %s, %s, ...)\n"), fd, kprop_version, name); free(name); } retval = krb5_auth_con_init(context, &auth_context); if (retval) { syslog(LOG_ERR, gettext("Error in krb5_auth_con_init: %s"), error_message(retval)); exit(1); } retval = krb5_auth_con_setflags(context, auth_context, KRB5_AUTH_CONTEXT_DO_SEQUENCE); if (retval) { syslog(LOG_ERR, gettext("Error in krb5_auth_con_setflags: %s"), error_message(retval)); exit(1); } retval = krb5_auth_con_setaddrs(context, auth_context, &receiver_addr, &sender_addr); if (retval) { syslog(LOG_ERR, gettext("Error in krb5_auth_con_setaddrs: %s"), error_message(retval)); exit(1); } if (srvtab) { retval = krb5_kt_resolve(context, srvtab, &keytab); if (retval) { syslog(LOG_ERR, gettext("Error in krb5_kt_resolve: %s"), error_message(retval)); exit(1); } } retval = krb5_recvauth(context, &auth_context, (void *) &fd, kprop_version, server, 0, keytab, &ticket); if (retval) { syslog(LOG_ERR, gettext("Error in krb5_recvauth: %s"), error_message(retval)); exit(1); } retval = krb5_copy_principal(context, ticket->enc_part2->client, clientp); if (retval) { syslog(LOG_ERR, gettext("Error in krb5_copy_prinicpal: %s"), error_message(retval)); exit(1); } *etype = ticket->enc_part.enctype; if (debug) { char * name; char etypebuf[100]; retval = krb5_unparse_name(context, *clientp, &name); if (retval) { /* Solaris Kerberos: Keep error messages consistent */ com_err(progname, retval, gettext("while unparsing client name")); exit(1); } retval = krb5_enctype_to_string(*etype, etypebuf, sizeof(etypebuf)); if (retval) { /* Solaris Kerberos: Keep error messages consistent */ com_err(progname, retval, gettext("while unparsing ticket etype")); exit(1); } printf("authenticated client: %s (etype == %s)\n", name, etypebuf); free(name); } krb5_free_ticket(context, ticket); } krb5_boolean authorized_principal(context, p, auth_etype) krb5_context context; krb5_principal p; krb5_enctype auth_etype; { char *name, *ptr; char buf[1024]; krb5_error_code retval; FILE *acl_file; int end; krb5_enctype acl_etype; retval = krb5_unparse_name(context, p, &name); if (retval) return FALSE; acl_file = fopen(acl_file_name, "r"); if (!acl_file) return FALSE; while (!feof(acl_file)) { if (!fgets(buf, sizeof(buf), acl_file)) break; end = strlen(buf) - 1; if (buf[end] == '\n') buf[end] = '\0'; if (!strncmp(name, buf, strlen(name))) { ptr = buf+strlen(name); /* if the next character is not whitespace or nul, then the match is only partial. continue on to new lines. */ if (*ptr && !isspace((int) *ptr)) continue; /* otherwise, skip trailing whitespace */ for (; *ptr && isspace((int) *ptr); ptr++) ; /* now, look for an etype string. if there isn't one, return true. if there is an invalid string, continue. If there is a valid string, return true only if it matches the etype passed in, otherwise continue */ if ((*ptr) && ((retval = krb5_string_to_enctype(ptr, &acl_etype)) || (acl_etype != auth_etype))) continue; free(name); fclose(acl_file); return TRUE; } } free(name); fclose(acl_file); return FALSE; } void recv_database(context, fd, database_fd, confmsg) krb5_context context; int fd; int database_fd; krb5_data *confmsg; { krb5_ui_4 database_size; /* This must be 4 bytes */ int received_size, n; char buf[1024]; krb5_data inbuf, outbuf; krb5_error_code retval; /* * Receive and decode size from client */ retval = krb5_read_message(context, (void *) &fd, &inbuf); if (retval) { send_error(context, fd, retval, gettext("while reading database size")); com_err(progname, retval, gettext("while reading size of database from client")); exit(1); } if (krb5_is_krb_error(&inbuf)) recv_error(context, &inbuf); retval = krb5_rd_safe(context,auth_context,&inbuf,&outbuf,NULL); if (retval) { send_error(context, fd, retval, gettext( "while decoding database size")); krb5_free_data_contents(context, &inbuf); com_err(progname, retval, gettext("while decoding database size from client")); exit(1); } memcpy((char *) &database_size, outbuf.data, sizeof(database_size)); krb5_free_data_contents(context, &inbuf); krb5_free_data_contents(context, &outbuf); database_size = ntohl(database_size); /* * Initialize the initial vector. */ retval = krb5_auth_con_initivector(context, auth_context); if (retval) { send_error(context, fd, retval, gettext( "failed while initializing i_vector")); com_err(progname, retval, gettext("while initializing i_vector")); exit(1); } /* * Now start receiving the database from the net */ received_size = 0; while (received_size < database_size) { retval = krb5_read_message(context, (void *) &fd, &inbuf); if (retval) { snprintf(buf, sizeof (buf), gettext("while reading database block starting at offset %d"), received_size); com_err(progname, retval, buf); send_error(context, fd, retval, buf); exit(1); } if (krb5_is_krb_error(&inbuf)) recv_error(context, &inbuf); retval = krb5_rd_priv(context, auth_context, &inbuf, &outbuf, NULL); if (retval) { snprintf(buf, sizeof (buf), gettext("while decoding database block starting at offset %d"), received_size); com_err(progname, retval, buf); send_error(context, fd, retval, buf); krb5_free_data_contents(context, &inbuf); exit(1); } n = write(database_fd, outbuf.data, outbuf.length); if (n < 0) { snprintf(buf, sizeof (buf), gettext( "while writing database block starting at offset %d"), received_size); send_error(context, fd, errno, buf); } else if (n != outbuf.length) { snprintf(buf, sizeof (buf), gettext( "incomplete write while writing database block starting at\n" "offset %d (%d written, %d expected)"), received_size, n, outbuf.length); send_error(context, fd, KRB5KRB_ERR_GENERIC, buf); } received_size += outbuf.length; /* SUNWresync121: our krb5...contents sets length to 0 */ krb5_free_data_contents(context, &inbuf); krb5_free_data_contents(context, &outbuf); } /* * OK, we've seen the entire file. Did we get too many bytes? */ if (received_size > database_size) { snprintf(buf, sizeof (buf), gettext("Received %d bytes, expected %d bytes for database file"), received_size, database_size); send_error(context, fd, KRB5KRB_ERR_GENERIC, buf); } /* * Create message acknowledging number of bytes received, but * don't send it until kdb5_util returns successfully. */ database_size = htonl(database_size); inbuf.data = (char *) &database_size; inbuf.length = sizeof(database_size); retval = krb5_mk_safe(context,auth_context,&inbuf,confmsg,NULL); if (retval) { com_err(progname, retval, gettext("while encoding # of receieved bytes")); send_error(context, fd, retval, gettext("while encoding # of received bytes")); exit(1); } } void send_error(context, fd, err_code, err_text) krb5_context context; int fd; krb5_error_code err_code; char *err_text; { krb5_error error; const char *text; krb5_data outbuf; char buf[1024]; memset((char *)&error, 0, sizeof(error)); krb5_us_timeofday(context, &error.stime, &error.susec); error.server = server; error.client = client; if (err_text) text = err_text; else text = error_message(err_code); error.error = err_code - ERROR_TABLE_BASE_krb5; if (error.error > 127) { error.error = KRB_ERR_GENERIC; if (err_text) { sprintf(buf, "%s %s", error_message(err_code), err_text); text = buf; } } error.text.length = strlen(text) + 1; error.text.data = malloc(error.text.length); if (error.text.data) { strcpy(error.text.data, text); if (!krb5_mk_error(context, &error, &outbuf)) { (void) krb5_write_message(context, (void *)&fd,&outbuf); krb5_free_data_contents(context, &outbuf); } free(error.text.data); } } void recv_error(context, inbuf) krb5_context context; krb5_data *inbuf; { krb5_error *error; krb5_error_code retval; retval = krb5_rd_error(context, inbuf, &error); if (retval) { com_err(progname, retval, gettext("while decoding error packet from client")); exit(1); } if (error->error == KRB_ERR_GENERIC) { if (error->text.data) fprintf(stderr, gettext("Generic remote error: %s\n"), error->text.data); } else if (error->error) { com_err(progname, error->error + ERROR_TABLE_BASE_krb5, gettext("signaled from server")); if (error->text.data) fprintf(stderr, gettext("Error text from client: %s\n"), error->text.data); } krb5_free_error(context, error); exit(1); } void load_database(context, kdb_util, database_file_name) krb5_context context; char *kdb_util; char *database_file_name; { static char *edit_av[10]; int error_ret, save_stderr = -1; int child_pid; int count; /* has been included, so BSD will be defined on BSD systems */ #if BSD > 0 && BSD <= 43 #ifndef WEXITSTATUS #define WEXITSTATUS(w) (w).w_retcode #endif union wait waitb; #else int waitb; #endif krb5_error_code retval; kdb_log_context *log_ctx; if (debug) printf(gettext("calling kdb_util to load database\n")); log_ctx = context->kdblog_context; edit_av[0] = kdb_util; count = 1; if (realm) { edit_av[count++] = "-r"; edit_av[count++] = realm; } edit_av[count++] = "load"; if (kerb_database) { edit_av[count++] = "-d"; edit_av[count++] = kerb_database; } if (log_ctx && (log_ctx->iproprole == IPROP_SLAVE)) { edit_av[count++] = "-i"; } edit_av[count++] = database_file_name; edit_av[count++] = NULL; switch(child_pid = fork()) { case -1: com_err(progname, errno, gettext("while trying to fork %s"), kdb_util); exit(1); /*NOTREACHED*/ case 0: if (!debug) { save_stderr = dup(2); close(0); close(1); close(2); open("/dev/null", O_RDWR); dup(0); dup(0); } execv(kdb_util, edit_av); retval = errno; if (!debug) dup2(save_stderr, 2); com_err(progname, retval, gettext("while trying to exec %s"), kdb_util); _exit(1); /*NOTREACHED*/ default: if (debug) printf(gettext("Child PID is %d\n"), child_pid); if (wait(&waitb) < 0) { com_err(progname, errno, gettext("while waiting for %s"), kdb_util); exit(1); } } error_ret = WEXITSTATUS(waitb); if (error_ret) { com_err(progname, 0, gettext("%s returned a bad exit status (%d)"), kdb_util, error_ret); exit(1); } return; } /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include "iprop.h" #include #include /* * Default timeout can be changed using clnt_control() */ static struct timeval TIMEOUT = { 25, 0 }; kdb_incr_result_t * iprop_get_updates_1(argp, clnt) kdb_last_t *argp; CLIENT *clnt; { static kdb_incr_result_t clnt_res; memset((char *)&clnt_res, 0, sizeof (clnt_res)); if (clnt_call(clnt, IPROP_GET_UPDATES, (xdrproc_t)xdr_kdb_last_t, (caddr_t)argp, (xdrproc_t)xdr_kdb_incr_result_t, (caddr_t)&clnt_res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return (&clnt_res); } kdb_fullresync_result_t * iprop_full_resync_1(argp, clnt) void *argp; CLIENT *clnt; { static kdb_fullresync_result_t clnt_res; memset((char *)&clnt_res, 0, sizeof (clnt_res)); if (clnt_call(clnt, IPROP_FULL_RESYNC, (xdrproc_t)xdr_void, (caddr_t)argp, (xdrproc_t)xdr_kdb_fullresync_result_t, (caddr_t)&clnt_res, TIMEOUT) != RPC_SUCCESS) { return (NULL); } return (&clnt_res); } /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include /* * convert a sockaddr_storage address to a krb5_address * Returns address of krbap if success, NULL if error. */ krb5_address * cvtkaddr(struct sockaddr_storage *ss, krb5_address *krbap) { switch (ss->ss_family) { case AF_INET: krbap->contents = (krb5_octet *) malloc(sizeof (ss2sin(ss)->sin_addr)); if (krbap->contents == NULL) return (NULL); memcpy((char *)krbap->contents, (char *)&(ss2sin(ss)->sin_addr), sizeof (ss2sin(ss)->sin_addr)); krbap->addrtype = ADDRTYPE_INET; krbap->length = sizeof (ss2sin(ss)->sin_addr); return (krbap); case AF_INET6: if (IN6_IS_ADDR_V4MAPPED(&ss2sin6(ss)->sin6_addr)) { /* coerce to IPv4 address */ krbap->contents = (krb5_octet *) malloc(IPV4_ADDR_LEN); if (krbap->contents == NULL) return (NULL); IN6_V4MAPPED_TO_IPADDR( &(ss2sin6(ss)->sin6_addr), *(ipaddr_t *)(krbap->contents)); krbap->addrtype = ADDRTYPE_INET; krbap->length = IPV4_ADDR_LEN; } else { krbap->contents = (krb5_octet *) malloc(sizeof (ss2sin6(ss)->sin6_addr)); if (krbap->contents == NULL) return (NULL); memcpy((char *)krbap->contents, (char *)&ss2sin6(ss)->sin6_addr, sizeof (ss2sin6(ss)->sin6_addr)); krbap->addrtype = ADDRTYPE_INET6; krbap->length = sizeof (ss2sin6(ss)->sin6_addr); } return (krbap); default: return (NULL); } }