svn rev #24259: branches/plugins2/ doc/ src/ src/config-files/ src/include/ src/include/krb5/ ...

ghudson@MIT.EDU ghudson at MIT.EDU
Thu Aug 26 19:19:41 EDT 2010


http://src.mit.edu/fisheye/changelog/krb5/?cs=24259
Commit By: ghudson
Log Message:
Merge trunk changes from r24202 to r24258 to plugins2 branch.


Changed Files:
U   branches/plugins2/doc/krb5conf.texinfo
U   branches/plugins2/src/Makefile.in
U   branches/plugins2/src/config-files/krb5.conf.M
U   branches/plugins2/src/include/Makefile.in
U   branches/plugins2/src/include/k5-int.h
U   branches/plugins2/src/include/krb5/krb5.hin
U   branches/plugins2/src/kdc/do_as_req.c
D   branches/plugins2/src/lib/gssapi/generic/util_canonhost.c
D   branches/plugins2/src/lib/gssapi/generic/util_localhost.c
U   branches/plugins2/src/lib/gssapi/krb5/acquire_cred.c
U   branches/plugins2/src/lib/gssapi/krb5/krb5_gss_glue.c
U   branches/plugins2/src/lib/kadm5/kadm_rpc_xdr.c
U   branches/plugins2/src/lib/krb5/error_tables/Makefile.in
A   branches/plugins2/src/lib/krb5/error_tables/k5e1_err.et
U   branches/plugins2/src/lib/krb5/krb/Makefile.in
U   branches/plugins2/src/lib/krb5/krb/gic_opt.c
U   branches/plugins2/src/lib/krb5/krb/gic_pwd.c
U   branches/plugins2/src/lib/krb5/krb/rd_req_dec.c
A   branches/plugins2/src/lib/krb5/krb/t_expire_warn.c
A   branches/plugins2/src/lib/krb5/krb/t_expire_warn.py
U   branches/plugins2/src/lib/krb5/libkrb5.exports
U   branches/plugins2/src/lib/rpc/xdr.c
U   branches/plugins2/src/plugins/kdb/ldap/libkdb_ldap/ldap_misc.c
U   branches/plugins2/src/util/k5test.py
_U  branches/plugins2/src/util/profile/
U   branches/plugins2/src/util/profile/prof_err.et
U   branches/plugins2/src/util/profile/prof_init.c
U   branches/plugins2/src/util/profile/prof_parse.c
U   branches/plugins2/src/util/profile/prof_test1
Modified: branches/plugins2/doc/krb5conf.texinfo
===================================================================
--- branches/plugins2/doc/krb5conf.texinfo	2010-08-26 16:59:37 UTC (rev 24258)
+++ branches/plugins2/doc/krb5conf.texinfo	2010-08-26 23:19:40 UTC (rev 24259)
@@ -40,6 +40,21 @@
 
 then the second value of foo (baz) would never be read.
 
+The @code{krb5.conf} file can include other files using either of the
+following directives at the beginning of a line:
+
+ at smallexample
+include @var{FILENAME}
+includedir @var{DIRNAME}
+ at end smallexample
+
+ at var{FILENAME} or @var{DIRNAME} should be an absolute path.  The named
+file or directory must exist and be readable.  Including a directory
+includes all files within the directory whose names consist solely of
+alphanumeric characters, dashes, or underscores.  Included profile files
+are syntactically independent of their parents, so each included file
+must begin with a section header.
+
 The @code{krb5.conf} file may contain any or all of the following 
 sections:
 

Modified: branches/plugins2/src/Makefile.in
===================================================================
--- branches/plugins2/src/Makefile.in	2010-08-26 16:59:37 UTC (rev 24258)
+++ branches/plugins2/src/Makefile.in	2010-08-26 23:19:40 UTC (rev 24259)
@@ -445,6 +445,7 @@
 	$(INC)asn1_err.h $(ET)asn1_err.c \
 	$(INC)kdb5_err.h $(ET)kdb5_err.c \
 	$(INC)krb5_err.h $(ET)krb5_err.c \
+	$(INC)k5e1_err.h $(ET)k5e1_err.c \
 	$(INC)kv5m_err.h $(ET)kv5m_err.c \
 	$(INC)krb524_err.h $(ET)krb524_err.c \
 	$(PR)prof_err.h $(PR)prof_err.c \
@@ -501,6 +502,8 @@
 	$(AWK) -f $(AH) outfile=$@ $(ET)kdb5_err.et
 $(INC)krb5_err.h: $(AH) $(ET)krb5_err.et
 	$(AWK) -f $(AH) outfile=$@ $(ET)krb5_err.et
+$(INC)k5e1_err.h: $(AH) $(ET)k5e1_err.et
+	$(AWK) -f $(AH) outfile=$@ $(ET)k5e1_err.et
 $(INC)kv5m_err.h: $(AH) $(ET)kv5m_err.et
 	$(AWK) -f $(AH) outfile=$@ $(ET)kv5m_err.et
 $(INC)krb524_err.h: $(AH) $(ET)krb524_err.et
@@ -524,6 +527,8 @@
 	$(AWK) -f $(AC) outfile=$@ $(ET)kdb5_err.et
 $(ET)krb5_err.c: $(AC) $(ET)krb5_err.et
 	$(AWK) -f $(AC) outfile=$@ $(ET)krb5_err.et
+$(ET)k5e1_err.c: $(AC) $(ET)k5e1_err.et
+	$(AWK) -f $(AC) outfile=$@ $(ET)k5e1_err.et
 $(ET)kv5m_err.c: $(AC) $(ET)kv5m_err.et
 	$(AWK) -f $(AC) outfile=$@ $(ET)kv5m_err.et
 $(ET)krb524_err.c: $(AC) $(ET)krb524_err.et
@@ -541,8 +546,8 @@
 $(CE)test2.c: $(AC) $(CE)test2.et
 	$(AWK) -f $(AC) outfile=$@ $(CE)test2.et
 
-KRBHDEP = $(INC)krb5\krb5.hin $(INC)krb5_err.h $(INC)kdb5_err.h \
-	$(INC)kv5m_err.h $(INC)krb524_err.h $(INC)asn1_err.h
+KRBHDEP = $(INC)krb5\krb5.hin $(INC)krb5_err.h $(INC)k5e1_err.h \
+	$(INC)kdb5_err.h $(INC)kv5m_err.h $(INC)krb524_err.h $(INC)asn1_err.h
 
 $(INC)krb5\krb5.h: $(KRBHDEP)
 	rm -f $@

Modified: branches/plugins2/src/config-files/krb5.conf.M
===================================================================
--- branches/plugins2/src/config-files/krb5.conf.M	2010-08-26 16:59:37 UTC (rev 24258)
+++ branches/plugins2/src/config-files/krb5.conf.M	2010-08-26 23:19:40 UTC (rev 24259)
@@ -60,6 +60,17 @@
 .sp
 
 .PP
+.I krb5.conf
+can include other files using the directives "include FILENAME" or
+"includedir DIRNAME", which must occur at the beginning of a line.
+FILENAME or DIRNAME should be an absolute path.  The named file or
+directory must exist and be readable.  Including a directory includes
+all files within the directory whose names consist solely of
+alphanumeric characters, dashes, or underscores.  Included profile
+files are syntactically independent of their parents, so each included
+file must begin with a section header.
+
+.PP
 The following sections are currently used in the 
 .I krb5.conf
 file:

Modified: branches/plugins2/src/include/Makefile.in
===================================================================
--- branches/plugins2/src/include/Makefile.in	2010-08-26 16:59:37 UTC (rev 24258)
+++ branches/plugins2/src/include/Makefile.in	2010-08-26 23:19:40 UTC (rev 24259)
@@ -17,9 +17,10 @@
 maybe-make-db.h-redirect:
 	test -r db.h || echo '#include <@DB_HEADER@>' > db.h
 
-ET_HEADERS = adm_err.h asn1_err.h kdb5_err.h krb5_err.h
+ET_HEADERS = adm_err.h asn1_err.h kdb5_err.h krb5_err.h k5e1_err.h
 K5_ET_HEADERS = \
 	../lib/krb5/error_tables/krb5_err.h \
+	../lib/krb5/error_tables/k5e1_err.h \
 	../lib/krb5/error_tables/kdb5_err.h \
 	../lib/krb5/error_tables/kv5m_err.h \
 	../lib/krb5/error_tables/krb524_err.h \
@@ -103,7 +104,7 @@
 
 #
 # Build the error table include files:
-# asn1_err.h kdb5_err.h krb5_err.h kv5m_err.h krb524_err.h
+# asn1_err.h kdb5_err.h krb5_err.h k5e1_err.h kv5m_err.h krb524_err.h
 
 $(K5_ET_HEADERS): rebuild-error-tables
 	: $@
@@ -114,8 +115,8 @@
 force:
 
 clean-unix::
-	$(RM) krb5/krb5.h krb5_err.h kdb5_err.h kv5m_err.h krb524_err.h \
-		asn1_err.h private-and-public-decls krb5.stamp
+	$(RM) krb5/krb5.h krb5_err.h k5e1_err.h kdb5_err.h kv5m_err.h \
+		krb524_err.h asn1_err.h private-and-public-decls krb5.stamp
 	$(RM) $(ET_HEADERS) autoconf.stamp
 
 clean-windows::

Modified: branches/plugins2/src/include/k5-int.h
===================================================================
--- branches/plugins2/src/include/k5-int.h	2010-08-26 16:59:37 UTC (rev 24258)
+++ branches/plugins2/src/include/k5-int.h	2010-08-26 23:19:40 UTC (rev 24259)
@@ -1147,6 +1147,8 @@
     char * fast_ccache_name;
     krb5_ccache out_ccache;
     krb5_flags fast_flags;
+    krb5_expire_callback_func *expire_cb;
+    void *expire_data;
 } krb5_gic_opt_private;
 
 /*

Modified: branches/plugins2/src/include/krb5/krb5.hin
===================================================================
--- branches/plugins2/src/include/krb5/krb5.hin	2010-08-26 16:59:37 UTC (rev 24258)
+++ branches/plugins2/src/include/krb5/krb5.hin	2010-08-26 23:19:40 UTC (rev 24259)
@@ -1003,6 +1003,8 @@
 #define KRB5_LRQ_ONE_LAST_REQ           (-5)
 #define KRB5_LRQ_ALL_PW_EXPTIME         6
 #define KRB5_LRQ_ONE_PW_EXPTIME         (-6)
+#define KRB5_LRQ_ALL_ACCT_EXPTIME       7
+#define KRB5_LRQ_ONE_ACCT_EXPTIME       (-7)
 
 /* PADATA types */
 #define KRB5_PADATA_NONE                0
@@ -2352,7 +2354,49 @@
 /* Fast flags*/
 #define KRB5_FAST_REQUIRED 1l<<0 /*!< Require KDC to support FAST*/
 
+typedef void
+krb5_expire_callback_func(krb5_context context, void *data,
+                          krb5_timestamp password_expiration,
+                          krb5_timestamp account_expiration,
+                          krb5_boolean is_last_req);
+
+/**
+ * Set a callback to receive password and account expiration times.
+ *
+ * This option only applies to krb5_get_init_creds_password().  @a cb will be
+ * invoked if and only if credentials are successfully acquired.  The callback
+ * will receive the @a context from the krb5_get_init_creds_password() call and
+ * the @a data argument supplied with this API.  The remaining arguments should
+ * be interpreted as follows:
+ *
+ * If @a is_last_req is true, then the KDC reply contained last-req entries
+ * which unambiguously indicated the password expiration, account expiration,
+ * or both.  (If either value was not present, the corresponding argument will
+ * be 0.)  Furthermore, a non-zero @a password_expiration should be taken as a
+ * suggestion from the KDC that a warning be displayed.
+ *
+ * If @a is_last_req is false, then @a account_expiration will be 0 and @a
+ * password_expiration will contain the expiration time of either the password
+ * or account, or 0 if no expiration time was indicated in the KDC reply.  The
+ * callback should independently decide whether to display a password
+ * expiration warning.
+ *
+ * Note that @a cb may be invoked even if credentials are being acquired for
+ * the kadmin/changepw service in order to change the password.  It is the
+ * caller's responsibility to avoid displaying a password expiry warning in
+ * this case.
+ *
+ * Setting an expire callback with this API will cause
+ * krb5_get_init_creds_password() not to send password expiry warnings to the
+ * prompter, as it ordinarily may.
+ */
 krb5_error_code KRB5_CALLCONV
+krb5_get_init_creds_opt_set_expire_callback(krb5_context context,
+                                            krb5_get_init_creds_opt *opt,
+                                            krb5_expire_callback_func cb,
+                                            void *data);
+
+krb5_error_code KRB5_CALLCONV
 krb5_get_init_creds_password(krb5_context context, krb5_creds *creds,
                              krb5_principal client, char *password,
                              krb5_prompter_fct prompter, void *data,

Modified: branches/plugins2/src/kdc/do_as_req.c
===================================================================
--- branches/plugins2/src/kdc/do_as_req.c	2010-08-26 16:59:37 UTC (rev 24258)
+++ branches/plugins2/src/kdc/do_as_req.c	2010-08-26 23:19:40 UTC (rev 24259)
@@ -88,6 +88,17 @@
                  int, krb5_data *, krb5_principal, krb5_data **,
                  const char *);
 
+/* Determine the key-expiration value according to RFC 4120 section 5.4.2. */
+static krb5_timestamp
+get_key_exp(krb5_db_entry *entry)
+{
+    if (entry->expiration == 0)
+        return entry->pw_expiration;
+    if (entry->pw_expiration == 0)
+        return entry->expiration;
+    return min(entry->expiration, entry->pw_expiration);
+}
+
 /*ARGSUSED*/
 krb5_error_code
 process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
@@ -541,7 +552,7 @@
         goto errout;
     }
     reply_encpart.nonce = request->nonce;
-    reply_encpart.key_exp = client->expiration;
+    reply_encpart.key_exp = get_key_exp(client);
     reply_encpart.flags = enc_tkt_reply.flags;
     reply_encpart.server = ticket_reply.server;
 

Modified: branches/plugins2/src/lib/gssapi/krb5/acquire_cred.c
===================================================================
--- branches/plugins2/src/lib/gssapi/krb5/acquire_cred.c	2010-08-26 16:59:37 UTC (rev 24258)
+++ branches/plugins2/src/lib/gssapi/krb5/acquire_cred.c	2010-08-26 23:19:40 UTC (rev 24259)
@@ -103,20 +103,19 @@
                                        const gss_OID desired_object,
                                        gss_buffer_t value)
 {
-    char *new, *old;
+    char *new = NULL, *old;
     int err;
 
     err = gss_krb5int_initialize_library();
     if (err != 0)
         return GSS_S_FAILURE;
 
-    if (value->value == NULL)
-        return GSS_S_FAILURE;
+    if (value->value != NULL) {
+        new = strdup((char *)value->value);
+        if (new == NULL)
+            return GSS_S_FAILURE;
+    }
 
-    new = strdup((char *)value->value);
-    if (new == NULL)
-        return GSS_S_FAILURE;
-
     err = k5_mutex_lock(&gssint_krb5_keytab_lock);
     if (err) {
         free(new);
@@ -125,8 +124,7 @@
     old = krb5_gss_keytab;
     krb5_gss_keytab = new;
     k5_mutex_unlock(&gssint_krb5_keytab_lock);
-    if (old != NULL)
-        free(old);
+    free(old);
     return GSS_S_COMPLETE;
 }
 

Modified: branches/plugins2/src/lib/gssapi/krb5/krb5_gss_glue.c
===================================================================
--- branches/plugins2/src/lib/gssapi/krb5/krb5_gss_glue.c	2010-08-26 16:59:37 UTC (rev 24258)
+++ branches/plugins2/src/lib/gssapi/krb5/krb5_gss_glue.c	2010-08-26 23:19:40 UTC (rev 24259)
@@ -253,7 +253,7 @@
     OM_uint32 minor_status;
     gss_buffer_desc req_buffer;
 
-    req_buffer.length = strlen(keytab);
+    req_buffer.length = (keytab == NULL) ? 0 : strlen(keytab);
     req_buffer.value = (char *)keytab;
 
     major_status = gssspi_mech_invoke(&minor_status,

Modified: branches/plugins2/src/lib/kadm5/kadm_rpc_xdr.c
===================================================================
--- branches/plugins2/src/lib/kadm5/kadm_rpc_xdr.c	2010-08-26 16:59:37 UTC (rev 24258)
+++ branches/plugins2/src/lib/kadm5/kadm_rpc_xdr.c	2010-08-26 23:19:40 UTC (rev 24259)
@@ -1021,7 +1021,7 @@
     * is safe.
     */
 
-   if (!xdr_u_int(xdrs, (unsigned int *) objp))
+   if (!xdr_int32(xdrs, (int32_t *) objp))
 	return (FALSE);
    return (TRUE);
 }

Modified: branches/plugins2/src/lib/krb5/error_tables/Makefile.in
===================================================================
--- branches/plugins2/src/lib/krb5/error_tables/Makefile.in	2010-08-26 16:59:37 UTC (rev 24258)
+++ branches/plugins2/src/lib/krb5/error_tables/Makefile.in	2010-08-26 23:19:40 UTC (rev 24259)
@@ -10,14 +10,15 @@
 EHDRDIR=$(BUILDTOP)$(S)include
 ETDIR=$(top_srcdir)$(S)util$(S)et
 
-STLIBOBJS= asn1_err.o kdb5_err.o krb5_err.o \
+STLIBOBJS= asn1_err.o kdb5_err.o krb5_err.o k5e1_err.o \
       kv5m_err.o krb524_err.o
 
-HDRS= asn1_err.h kdb5_err.h krb5_err.h kv5m_err.h krb524_err.h
-OBJS= $(OUTPRE)asn1_err.$(OBJEXT) $(OUTPRE)kdb5_err.$(OBJEXT) $(OUTPRE)krb5_err.$(OBJEXT) \
+HDRS= asn1_err.h kdb5_err.h krb5_err.h k5e1_err.h kv5m_err.h krb524_err.h
+OBJS= $(OUTPRE)asn1_err.$(OBJEXT) $(OUTPRE)kdb5_err.$(OBJEXT) \
+      $(OUTPRE)krb5_err.$(OBJEXT) $(OUTPRE)k5e1_err.$(OBJEXT) \
       $(OUTPRE)kv5m_err.$(OBJEXT) $(OUTPRE)krb524_err.$(OBJEXT)
-ETSRCS= asn1_err.c kdb5_err.c krb5_err.c kv5m_err.c krb524_err.c
-SRCS= asn1_err.c kdb5_err.c krb5_err.c kv5m_err.c krb524_err.c
+ETSRCS= asn1_err.c kdb5_err.c krb5_err.c k5e1_err.c kv5m_err.c krb524_err.c
+SRCS= asn1_err.c kdb5_err.c krb5_err.c k5e1_err.c kv5m_err.c krb524_err.c
 
 ##DOS##LIBOBJS = $(OBJS)
 
@@ -30,16 +31,19 @@
 	$(AWK) -f $(ETDIR)/et_h.awk outfile=asn1_err.h asn1_err.et
 	$(AWK) -f $(ETDIR)/et_h.awk outfile=kdb5_err.h kdb5_err.et
 	$(AWK) -f $(ETDIR)/et_h.awk outfile=krb5_err.h krb5_err.et
+	$(AWK) -f $(ETDIR)/et_h.awk outfile=k5e1_err.h k5e1_err.et
 	$(AWK) -f $(ETDIR)/et_h.awk outfile=kv5m_err.h kv5m_err.et
 	$(AWK) -f $(ETDIR)/et_h.awk outfile=krb524_err.h krb524_err.et
 	$(AWK) -f $(ETDIR)/et_c.awk outfile=asn1_err.c asn1_err.et
 	$(AWK) -f $(ETDIR)/et_c.awk outfile=kdb5_err.c kdb5_err.et
 	$(AWK) -f $(ETDIR)/et_c.awk outfile=krb5_err.c krb5_err.et
+	$(AWK) -f $(ETDIR)/et_c.awk outfile=k5e1_err.c k5e1_err.et
 	$(AWK) -f $(ETDIR)/et_c.awk outfile=kv5m_err.c kv5m_err.et
 	$(AWK) -f $(ETDIR)/et_c.awk outfile=krb524_err.c krb524_err.et
 	if exist asn1_err.h copy asn1_err.h "$(EHDRDIR)"
 	if exist kdb5_err.h copy kdb5_err.h "$(EHDRDIR)"
 	if exist krb5_err.h copy krb5_err.h "$(EHDRDIR)"
+	if exist k5e1_err.h copy k5e1_err.h "$(EHDRDIR)"
 	if exist kv5m_err.h copy kv5m_err.h "$(EHDRDIR)"
 	if exist krb524_err.h copy krb524_err.h "$(EHDRDIR)"
 
@@ -49,6 +53,7 @@
 $(OUTPRE)asn1_err.$(OBJEXT): asn1_err.c
 $(OUTPRE)kdb5_err.$(OBJEXT): kdb5_err.c
 $(OUTPRE)krb5_err.$(OBJEXT): krb5_err.c
+$(OUTPRE)k5e1_err.$(OBJEXT): k5e1_err.c
 $(OUTPRE)kv5m_err.$(OBJEXT): kv5m_err.c
 $(OUTPRE)krb524_err.$(OBJEXT): krb524_err.c
 

Copied: branches/plugins2/src/lib/krb5/error_tables/k5e1_err.et (from rev 24258, trunk/src/lib/krb5/error_tables/k5e1_err.et)
===================================================================
--- branches/plugins2/src/lib/krb5/error_tables/k5e1_err.et	                        (rev 0)
+++ branches/plugins2/src/lib/krb5/error_tables/k5e1_err.et	2010-08-26 23:19:40 UTC (rev 24259)
@@ -0,0 +1,33 @@
+#
+# lib/krb5/error_tables/k5e1_err.et
+#
+# Copyright 2010 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.
+# 
+#
+# The Kerberos v5 library error code expansion table (#1).
+# This table exists to hold new libkrb5 error codes since the
+# original krb5 error table is full.
+#
+error_table k5e1
+
+end

Modified: branches/plugins2/src/lib/krb5/krb/Makefile.in
===================================================================
--- branches/plugins2/src/lib/krb5/krb/Makefile.in	2010-08-26 16:59:37 UTC (rev 24258)
+++ branches/plugins2/src/lib/krb5/krb/Makefile.in	2010-08-26 23:19:40 UTC (rev 24259)
@@ -386,6 +386,9 @@
 t_etypes: $(T_ETYPES_OBJS) $(KRB5_BASE_DEPLIBS)
 	$(CC_LINK) -o t_etypes $(T_ETYPES_OBJS) $(KRB5_BASE_LIBS)
 
+t_expire_warn: t_expire_warn.o $(KRB5_BASE_DEPLIBS)
+	$(CC_LINK) -o $@ t_expire_warn.o $(KRB5_BASE_LIBS)
+
 TEST_PROGS= t_walk_rtree t_kerb t_ser t_deltat t_expand t_authdata t_pac \
 	t_princ t_etypes
 
@@ -426,6 +429,9 @@
 	$(RUN_SETUP) $(VALGRIND) ./t_princ
 	$(RUN_SETUP) $(VALGRIND) ./t_etypes
 
+check-pytests:: t_expire_warn
+	$(RUNPYTEST) $(srcdir)/t_expire_warn.py $(PYTESTFLAGS)
+
 clean::
 	$(RM) $(OUTPRE)t_walk_rtree$(EXEEXT) $(OUTPRE)t_walk_rtree.$(OBJEXT) \
 		$(OUTPRE)t_kerb$(EXEEXT) $(OUTPRE)t_kerb.$(OBJEXT)	\

Modified: branches/plugins2/src/lib/krb5/krb/gic_opt.c
===================================================================
--- branches/plugins2/src/lib/krb5/krb/gic_opt.c	2010-08-26 16:59:37 UTC (rev 24258)
+++ branches/plugins2/src/lib/krb5/krb/gic_opt.c	2010-08-26 23:19:40 UTC (rev 24259)
@@ -480,3 +480,22 @@
     *out_flags = opte->opt_private->fast_flags;
     return retval;
 }
+
+krb5_error_code KRB5_CALLCONV
+krb5_get_init_creds_opt_set_expire_callback(krb5_context context,
+                                            krb5_get_init_creds_opt *opt,
+                                            krb5_expire_callback_func cb,
+                                            void *data)
+{
+    krb5_error_code retval = 0;
+    krb5_gic_opt_ext *opte;
+
+    retval = krb5int_gic_opt_to_opte(context, opt, &opte, 0,
+                                     "krb5_get_init_creds_opt_set_"
+                                     "expire_callback");
+    if (retval)
+        return retval;
+    opte->opt_private->expire_cb = cb;
+    opte->opt_private->expire_data = data;
+    return retval;
+}

Modified: branches/plugins2/src/lib/krb5/krb/gic_pwd.c
===================================================================
--- branches/plugins2/src/lib/krb5/krb/gic_pwd.c	2010-08-26 16:59:37 UTC (rev 24258)
+++ branches/plugins2/src/lib/krb5/krb/gic_pwd.c	2010-08-26 23:19:40 UTC (rev 24259)
@@ -56,12 +56,10 @@
 
         /* PROMPTER_INVOCATION */
         krb5int_set_prompt_types(context, &prompt_type);
-        if ((ret = (((*prompter)(context, prompter_data, NULL, NULL,
-                                 1, &prompt))))) {
-            krb5int_set_prompt_types(context, 0);
+        ret = (*prompter)(context, prompter_data, NULL, NULL, 1, &prompt);
+        krb5int_set_prompt_types(context, 0);
+        if (ret)
             return(ret);
-        }
-        krb5int_set_prompt_types(context, 0);
     }
 
     if ((salt->length == -1 || salt->length == SALT_TYPE_AFS_LENGTH) && (salt->data == NULL)) {
@@ -106,6 +104,111 @@
     return 0;
 }
 
+/* Return the password expiry time indicated by enc_part2.  Set *is_last_req
+ * if the information came from a last_req value. */
+static void
+get_expiry_times(krb5_enc_kdc_rep_part *enc_part2, krb5_timestamp *pw_exp,
+                 krb5_timestamp *acct_exp, krb5_boolean *is_last_req)
+{
+    krb5_last_req_entry **last_req;
+    krb5_int32 lr_type;
+
+    *pw_exp = 0;
+    *acct_exp = 0;
+    *is_last_req = FALSE;
+
+    /* Look for last-req entries for password or account expiration. */
+    if (enc_part2->last_req) {
+        for (last_req = enc_part2->last_req; *last_req; last_req++) {
+            lr_type = (*last_req)->lr_type;
+            if (lr_type == KRB5_LRQ_ALL_PW_EXPTIME ||
+                lr_type == KRB5_LRQ_ONE_PW_EXPTIME) {
+                *is_last_req = TRUE;
+                *pw_exp = (*last_req)->value;
+            } else if (lr_type == KRB5_LRQ_ALL_ACCT_EXPTIME ||
+                       lr_type == KRB5_LRQ_ONE_ACCT_EXPTIME) {
+                *is_last_req = TRUE;
+                *acct_exp = (*last_req)->value;
+            }
+        }
+    }
+
+    /* If we didn't find any, use the ambiguous key_exp field. */
+    if (*is_last_req == FALSE)
+        *pw_exp = enc_part2->key_exp;
+}
+
+/*
+ * Send an appropriate warning prompter if as_reply indicates that the password
+ * is going to expire soon.  If an expire callback was provided, use that
+ * instead.
+ */
+static void
+warn_pw_expiry(krb5_context context, krb5_get_init_creds_opt *options,
+               krb5_prompter_fct prompter, void *data,
+               const char *in_tkt_service, krb5_kdc_rep *as_reply)
+{
+    krb5_error_code ret;
+    krb5_timestamp pw_exp, acct_exp, now;
+    krb5_boolean is_last_req;
+    krb5_deltat delta;
+    krb5_gic_opt_ext *opte;
+    char ts[256], banner[1024];
+
+    get_expiry_times(as_reply->enc_part2, &pw_exp, &acct_exp, &is_last_req);
+
+    ret = krb5int_gic_opt_to_opte(context, options, &opte, 0, "");
+    if (ret == 0 && opte->opt_private->expire_cb != NULL) {
+        krb5_expire_callback_func *cb = opte->opt_private->expire_cb;
+        void *cb_data = opte->opt_private->expire_data;
+
+        /* Invoke the expire callback and don't send prompter warnings. */
+        (*cb)(context, cb_data, pw_exp, acct_exp, is_last_req);
+        return;
+    }
+
+    /* Don't warn if the password is being changed. */
+    if (in_tkt_service && strcmp(in_tkt_service, "kadmin/changepw") == 0)
+        return;
+
+    /*
+     * If the expiry time came from a last_req field, assume the KDC wants us
+     * to warn.  Otherwise, warn only if the expiry time is less than a week
+     * from now.
+     */
+    ret = krb5_timeofday(context, &now);
+    if (ret != 0)
+        return;
+    if (!is_last_req &&
+        (pw_exp < now || (pw_exp - now) > 7 * 24 * 60 * 60))
+        return;
+
+    if (!prompter)
+        return;
+
+    ret = krb5_timestamp_to_string(pw_exp, ts, sizeof(ts));
+    if (ret != 0)
+        return;
+
+    delta = pw_exp - now;
+    if (delta < 3600) {
+        snprintf(banner, sizeof(banner),
+                 "Warning: Your password will expire in less than one hour "
+                 "on %s", ts);
+    } else if (delta < 86400*2) {
+        snprintf(banner, sizeof(banner),
+                 "Warning: Your password will expire in %d hour%s on %s",
+                 delta / 3600, delta < 7200 ? "" : "s", ts);
+    } else {
+        snprintf(banner, sizeof(banner),
+                 "Warning: Your password will expire in %d days on %s",
+                 delta / 86400, ts);
+    }
+
+    /* PROMPTER_INVOCATION */
+    (*prompter)(context, data, 0, banner, 0, 0);
+}
+
 krb5_error_code KRB5_CALLCONV
 krb5_get_init_creds_password(krb5_context context,
                              krb5_creds *creds,
@@ -263,12 +366,12 @@
 
         /* PROMPTER_INVOCATION */
         krb5int_set_prompt_types(context, prompt_types);
-        if ((ret = ((*prompter)(context, data, 0, banner,
-                                sizeof(prompt)/sizeof(prompt[0]), prompt))))
+        ret = (*prompter)(context, data, 0, banner,
+                          sizeof(prompt)/sizeof(prompt[0]), prompt);
+        krb5int_set_prompt_types(context, 0);
+        if (ret)
             goto cleanup;
-        krb5int_set_prompt_types(context, 0);
 
-
         if (strcmp(pw0.data, pw1.data) != 0) {
             ret = KRB5_LIBOS_BADPWDMATCH;
             snprintf(banner, sizeof(banner),
@@ -334,84 +437,14 @@
                                  start_time, in_tkt_service, options,
                                  krb5_get_as_key_password, (void *) &pw0,
                                  &use_master, &as_reply);
+    if (ret)
+        goto cleanup;
 
 cleanup:
-    krb5int_set_prompt_types(context, 0);
-    /* if getting the password was successful, then check to see if the
-       password is about to expire, and warn if so */
+    if (ret == 0)
+        warn_pw_expiry(context, options, prompter, data, in_tkt_service,
+                       as_reply);
 
-    if (ret == 0) {
-        krb5_timestamp now;
-        krb5_last_req_entry **last_req;
-        int hours;
-
-        /* XXX 7 days should be configurable.  This is all pretty ad hoc,
-           and could probably be improved if I was willing to screw around
-           with timezones, etc. */
-
-        if (prompter &&
-            (!in_tkt_service ||
-             (strcmp(in_tkt_service, "kadmin/changepw") != 0)) &&
-            ((ret = krb5_timeofday(context, &now)) == 0) &&
-            as_reply->enc_part2->key_exp &&
-            ((hours = ((as_reply->enc_part2->key_exp-now)/(60*60))) <= 7*24) &&
-            (hours >= 0)) {
-            if (hours < 1)
-                snprintf(banner, sizeof(banner),
-                         "Warning: Your password will expire in less than one hour.");
-            else if (hours <= 48)
-                snprintf(banner, sizeof(banner),
-                         "Warning: Your password will expire in %d hour%s.",
-                         hours, (hours == 1)?"":"s");
-            else
-                snprintf(banner, sizeof(banner),
-                         "Warning: Your password will expire in %d days.",
-                         hours/24);
-
-            /* ignore an error here */
-            /* PROMPTER_INVOCATION */
-            (*prompter)(context, data, 0, banner, 0, 0);
-        } else if (prompter &&
-                   (!in_tkt_service ||
-                    (strcmp(in_tkt_service, "kadmin/changepw") != 0)) &&
-                   as_reply->enc_part2 && as_reply->enc_part2->last_req) {
-            /*
-             * Check the last_req fields
-             */
-
-            for (last_req = as_reply->enc_part2->last_req; *last_req; last_req++)
-                if ((*last_req)->lr_type == KRB5_LRQ_ALL_PW_EXPTIME ||
-                    (*last_req)->lr_type == KRB5_LRQ_ONE_PW_EXPTIME) {
-                    krb5_deltat delta;
-                    char ts[256];
-
-                    if ((ret = krb5_timeofday(context, &now)))
-                        break;
-
-                    if ((ret = krb5_timestamp_to_string((*last_req)->value,
-                                                        ts, sizeof(ts))))
-                        break;
-
-                    delta = (*last_req)->value - now;
-                    if (delta < 3600)
-                        snprintf(banner, sizeof(banner),
-                                 "Warning: Your password will expire in less than one hour on %s",
-                                 ts);
-                    else if (delta < 86400*2)
-                        snprintf(banner, sizeof(banner),
-                                 "Warning: Your password will expire in %d hour%s on %s",
-                                 delta / 3600, delta < 7200 ? "" : "s", ts);
-                    else
-                        snprintf(banner, sizeof(banner),
-                                 "Warning: Your password will expire in %d days on %s",
-                                 delta / 86400, ts);
-                    /* ignore an error here */
-                    /* PROMPTER_INVOCATION */
-                    (*prompter)(context, data, 0, banner, 0, 0);
-                }
-        }
-    }
-
     if (chpw_opts)
         krb5_get_init_creds_opt_free(context, chpw_opts);
     memset(pw0array, 0, sizeof(pw0array));

Modified: branches/plugins2/src/lib/krb5/krb/rd_req_dec.c
===================================================================
--- branches/plugins2/src/lib/krb5/krb/rd_req_dec.c	2010-08-26 16:59:37 UTC (rev 24258)
+++ branches/plugins2/src/lib/krb5/krb/rd_req_dec.c	2010-08-26 23:19:40 UTC (rev 24259)
@@ -44,7 +44,14 @@
  *
  *  server specifies the expected server's name for the ticket; if NULL, then
  *  any server will be accepted if the key can be found, and the caller should
- *  verify that the principal is something it trusts.
+ *  verify that the principal is something it trusts. With the exception of the
+ *  kdb keytab, the ticket's server field need not match the name passed in for
+ *  server. All that is required is that the ticket be encrypted with a key
+ *  from the keytab associated with the specified server principal. This
+ *  permits the KDC to have a set of aliases for the server without keeping
+ *  this information consistent with the server. So, when server is non-null,
+ *  the principal expected by the application needs to be consistent with the
+ *  local keytab, but not with the informational name in the ticket.
  *
  *  rcache specifies a replay detection cache used to store authenticators and
  *  server names

Copied: branches/plugins2/src/lib/krb5/krb/t_expire_warn.c (from rev 24258, trunk/src/lib/krb5/krb/t_expire_warn.c)
===================================================================
--- branches/plugins2/src/lib/krb5/krb/t_expire_warn.c	                        (rev 0)
+++ branches/plugins2/src/lib/krb5/krb/t_expire_warn.c	2010-08-26 23:19:40 UTC (rev 24259)
@@ -0,0 +1,90 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * lib/krb5/krb/t_expire_warn.c
+ *
+ * Copyright (C) 2010 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.
+ *
+ *
+ * Test harness for password expiration warnings.
+ */
+
+#include "k5-int.h"
+
+static int exp_dummy, prompt_dummy;
+
+static krb5_error_code
+prompter_cb(krb5_context ctx, void *data, const char *name,
+	    const char *banner, int num_prompts, krb5_prompt prompts[])
+{
+    /* Not expecting any actual prompts, only banners. */
+    assert(num_prompts == 0);
+    assert(banner != NULL);
+    printf("Prompter: %s\n", banner);
+    return 0;
+}
+
+static void
+expire_cb(krb5_context ctx, void *data, krb5_timestamp password_expiration,
+	  krb5_timestamp account_expiration, krb5_boolean is_last_req)
+{
+    printf("password_expiration = %ld\n", (long)password_expiration);
+    printf("account_expiration = %ld\n", (long)account_expiration);
+    printf("is_last_req = %d\n", (int)is_last_req);
+}
+
+int
+main(int argc, char **argv)
+{
+    krb5_context ctx;
+    krb5_get_init_creds_opt *opt;
+    char *user, *password, *service = NULL;
+    krb5_boolean use_cb;
+    krb5_principal client;
+    krb5_creds creds;
+
+    if (argc < 4) {
+	fprintf(stderr, "Usage: %s username password {1|0} [service]\n",
+		argv[0]);
+	return 1;
+    }
+    user = argv[1];
+    password = argv[2];
+    use_cb = atoi(argv[3]);
+    if (argc >= 5)
+	service = argv[4];
+
+    assert(krb5_init_context(&ctx) == 0);
+    assert(krb5_get_init_creds_opt_alloc(ctx, &opt) == 0);
+    if (use_cb) {
+	assert(krb5_get_init_creds_opt_set_expire_callback(ctx, opt, expire_cb,
+							   &exp_dummy) == 0);
+    }
+    assert(krb5_parse_name(ctx, user, &client) == 0);
+    assert(krb5_get_init_creds_password(ctx, &creds, client, password,
+					prompter_cb, &prompt_dummy, 0, service,
+					opt) == 0);
+    krb5_get_init_creds_opt_free(ctx, opt);
+    krb5_free_principal(ctx, client);
+    krb5_free_cred_contents(ctx, &creds);
+    return 0;
+}

Copied: branches/plugins2/src/lib/krb5/krb/t_expire_warn.py (from rev 24258, trunk/src/lib/krb5/krb/t_expire_warn.py)
===================================================================
--- branches/plugins2/src/lib/krb5/krb/t_expire_warn.py	                        (rev 0)
+++ branches/plugins2/src/lib/krb5/krb/t_expire_warn.py	2010-08-26 23:19:40 UTC (rev 24259)
@@ -0,0 +1,62 @@
+# Copyright (C) 2010 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.
+
+#!/usr/bin/python
+from k5test import *
+
+# Create a bare-bones KDC.
+realm = K5Realm(create_user=False, create_host=False, start_kadmind=False)
+
+# Create principals with various password expirations.
+realm.run_kadminl('addprinc -pw pass noexpire')
+realm.run_kadminl('addprinc -pw pass -pwexpire "30 minutes" minutes')
+realm.run_kadminl('addprinc -pw pass -pwexpire "12 hours" hours')
+realm.run_kadminl('addprinc -pw pass -pwexpire "3 days" days')
+
+# Check for expected prompter warnings when no expire callback is used.
+output = realm.run_as_client(['./t_expire_warn', 'noexpire', 'pass', '0'])
+if output:
+    fail('Unexpected output for noexpire')
+output = realm.run_as_client(['./t_expire_warn', 'minutes', 'pass', '0'])
+if ' less than one hour on ' not in output:
+    fail('Expected warning not seen for minutes')
+output = realm.run_as_client(['./t_expire_warn', 'hours', 'pass', '0'])
+if ' hours on ' not in output:
+    fail('Expected warning not seen for hours')
+output = realm.run_as_client(['./t_expire_warn', 'days', 'pass', '0'])
+if ' days on ' not in output:
+    fail('Expected warning not seen for days')
+
+# Check for expected expire callback behavior.  These tests are
+# carefully agnostic about whether the KDC supports last_req fields,
+# and could be made more specific if last_req support is added.
+output = realm.run_as_client(['./t_expire_warn', 'noexpire', 'pass', '1'])
+if 'password_expiration = 0\n' not in output or \
+        'account_expiration = 0\n' not in output or \
+        'is_last_req = ' not in output:
+    fail('Expected callback output not seen for noexpire')
+output = realm.run_as_client(['./t_expire_warn', 'days', 'pass', '1'])
+if 'password_expiration = ' not in output or \
+        'password_expiration = 0\n' in output:
+    fail('Expected non-zero password expiration not seen for days')
+
+success('Password expiration warning tests.')

Modified: branches/plugins2/src/lib/krb5/libkrb5.exports
===================================================================
--- branches/plugins2/src/lib/krb5/libkrb5.exports	2010-08-26 16:59:37 UTC (rev 24258)
+++ branches/plugins2/src/lib/krb5/libkrb5.exports	2010-08-26 23:19:40 UTC (rev 24259)
@@ -350,6 +350,7 @@
 krb5_get_init_creds_opt_set_canonicalize
 krb5_get_init_creds_opt_set_change_password_prompt
 krb5_get_init_creds_opt_set_etype_list
+krb5_get_init_creds_opt_set_expire_callback
 krb5_get_init_creds_opt_set_fast_ccache_name
 krb5_get_init_creds_opt_set_fast_flags
 krb5_get_init_creds_opt_set_forwardable

Modified: branches/plugins2/src/lib/rpc/xdr.c
===================================================================
--- branches/plugins2/src/lib/rpc/xdr.c	2010-08-26 16:59:37 UTC (rev 24258)
+++ branches/plugins2/src/lib/rpc/xdr.c	2010-08-26 23:19:40 UTC (rev 24259)
@@ -145,7 +145,7 @@
 		if (!XDR_GETLONG(xdrs, (long *) &l))
 			return (FALSE);
 
-		if (l > UINT_MAX)
+		if ((uint32_t)l > UINT_MAX)
 			return (FALSE);
 
 		*up = (u_int) l;

Modified: branches/plugins2/src/plugins/kdb/ldap/libkdb_ldap/ldap_misc.c
===================================================================
--- branches/plugins2/src/plugins/kdb/ldap/libkdb_ldap/ldap_misc.c	2010-08-26 16:59:37 UTC (rev 24258)
+++ branches/plugins2/src/plugins/kdb/ldap/libkdb_ldap/ldap_misc.c	2010-08-26 23:19:40 UTC (rev 24259)
@@ -2087,7 +2087,7 @@
             goto cleanup;
 
         if (attr_present == TRUE) {
-            if ((mask & KDB_PRINC_EXPIRE_TIME_ATTR) == 1) {
+            if (mask & KDB_PRINC_EXPIRE_TIME_ATTR) {
                 if (expiretime < entry->expiration)
                     entry->expiration = expiretime;
             } else {
@@ -2127,7 +2127,7 @@
             if ((st=krb5_dbe_lookup_last_pwd_change(context, entry, &last_pw_changed)) != 0)
                 goto cleanup;
 
-            if ((mask & KDB_PWD_EXPIRE_TIME_ATTR) == 1) {
+            if (mask & KDB_PWD_EXPIRE_TIME_ATTR) {
                 if ((last_pw_changed + pw_max_life) < entry->pw_expiration)
                     entry->pw_expiration = last_pw_changed + pw_max_life;
             } else

Modified: branches/plugins2/src/util/k5test.py
===================================================================
--- branches/plugins2/src/util/k5test.py	2010-08-26 16:59:37 UTC (rev 24258)
+++ branches/plugins2/src/util/k5test.py	2010-08-26 23:19:40 UTC (rev 24259)
@@ -119,7 +119,7 @@
 
 * start_kdc=False: Don't start the KDC.  Implies get_creds=False.
 
-* start_kadmin=False: Don't start kadmind.
+* start_kadmind=False: Don't start kadmind.
 
 * get_creds=False: Don't get user credentials.
 


Modified: svn:ignore
   - obj
darwin.exports
*.dylib
prof_err.h
prtest
prof_err.c
profile.h
test_parse
test_profile
lib*.so.*
Makefile
*.so
OBJS.*
binutils.versions
test?.ini
lib*.a
*.bak
profile_tcl
   + obj
darwin.exports
*.dylib
prof_err.h
prtest
prof_err.c
profile.h
test_parse
test_profile
test_include_dir
lib*.so.*
Makefile
*.so
OBJS.*
binutils.versions
test?.ini
lib*.a
*.bak
profile_tcl


Modified: branches/plugins2/src/util/profile/prof_err.et
===================================================================
--- branches/plugins2/src/util/profile/prof_err.et	2010-08-26 16:59:37 UTC (rev 24258)
+++ branches/plugins2/src/util/profile/prof_err.et	2010-08-26 23:19:40 UTC (rev 24259)
@@ -60,7 +60,13 @@
 error_code	PROF_BAD_BOOLEAN,		"Invalid boolean value"
 error_code	PROF_BAD_INTEGER,		"Invalid integer value"
 
+#
+# new error codes added at end to avoid changing values
+#
 error_code	PROF_MAGIC_FILE_DATA, "Bad magic value in profile_file_data_t"
+error_code	PROF_FAIL_INCLUDE_FILE,
+	"Included profile file could not be read"
+error_code	PROF_FAIL_INCLUDE_DIR,
+	"Included profile directory could not be read"
 
-
 end

Modified: branches/plugins2/src/util/profile/prof_init.c
===================================================================
--- branches/plugins2/src/util/profile/prof_init.c	2010-08-26 16:59:37 UTC (rev 24258)
+++ branches/plugins2/src/util/profile/prof_init.c	2010-08-26 23:19:40 UTC (rev 24259)
@@ -27,7 +27,7 @@
     const_profile_filespec_t *fs;
     profile_t profile;
     prf_file_t  new_file, last = 0;
-    errcode_t retval = 0;
+    errcode_t retval = 0, access_retval = 0;
 
     profile = malloc(sizeof(struct _profile_t));
     if (!profile)
@@ -43,9 +43,14 @@
         for (fs = files; !PROFILE_LAST_FILESPEC(*fs); fs++) {
             retval = profile_open_file(*fs, &new_file);
             /* if this file is missing, skip to the next */
-            if (retval == ENOENT || retval == EACCES || retval == EPERM) {
+            if (retval == ENOENT) {
                 continue;
             }
+            /* If we can't read this file, remember it but keep going. */
+            if (retval == EACCES || retval == EPERM) {
+                access_retval = retval;
+                continue;
+            }
             if (retval) {
                 profile_release(profile);
                 return retval;
@@ -58,11 +63,11 @@
         }
         /*
          * If last is still null after the loop, then all the files were
-         * missing, so return the appropriate error.
+         * missing or unreadable, so return the appropriate error.
          */
         if (!last) {
             profile_release(profile);
-            return ENOENT;
+            return access_retval ? access_retval : ENOENT;
         }
     }
 

Modified: branches/plugins2/src/util/profile/prof_parse.c
===================================================================
--- branches/plugins2/src/util/profile/prof_parse.c	2010-08-26 16:59:37 UTC (rev 24258)
+++ branches/plugins2/src/util/profile/prof_parse.c	2010-08-26 23:19:40 UTC (rev 24259)
@@ -1,6 +1,7 @@
 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
 #include "prof_int.h"
 
+#include <sys/types.h>
 #include <stdio.h>
 #include <string.h>
 #ifdef HAVE_STDLIB_H
@@ -8,6 +9,7 @@
 #endif
 #include <errno.h>
 #include <ctype.h>
+#include <dirent.h>
 
 #define SECTION_SEP_CHAR '/'
 
@@ -22,6 +24,8 @@
     struct profile_node *current_section;
 };
 
+static errcode_t parse_file(FILE *f, struct parse_state *state);
+
 static char *skip_over_blanks(char *cp)
 {
     while (*cp && isspace((int) (*cp)))
@@ -33,7 +37,7 @@
 {
     char *p = line + strlen(line);
     while (p > line && (p[-1] == '\n' || p[-1] == '\r'))
-        *p-- = 0;
+        *--p = 0;
 }
 
 static void parse_quoted_string(char *str)
@@ -66,14 +70,6 @@
 }
 
 
-static errcode_t parse_init_state(struct parse_state *state)
-{
-    state->state = STATE_INIT_COMMENT;
-    state->group_level = 0;
-
-    return profile_create_node("(root)", 0, &state->root_section);
-}
-
 static errcode_t parse_std_line(char *line, struct parse_state *state)
 {
     char    *cp, ch, *tag, *value;
@@ -201,10 +197,83 @@
     return 0;
 }
 
+/* Open and parse an included profile file. */
+static errcode_t parse_include_file(char *filename, struct parse_state *state)
+{
+    FILE    *fp;
+    errcode_t retval = 0;
+    struct parse_state incstate;
+
+    /* Create a new state so that fragments are syntactically independent,
+     * sharing the root section with the existing state. */
+    incstate.state = STATE_INIT_COMMENT;
+    incstate.group_level = 0;
+    incstate.root_section = state->root_section;
+    incstate.current_section = NULL;
+
+    fp = fopen(filename, "r");
+    if (fp == NULL)
+        return PROF_FAIL_INCLUDE_FILE;
+    retval = parse_file(fp, &incstate);
+    fclose(fp);
+    return retval;
+}
+
+/* Return non-zero if filename contains only alphanumeric characters, dashes,
+ * and underscores. */
+static int valid_name(const char *filename)
+{
+    const char *p;
+
+    for (p = filename; *p != '\0'; p++) {
+        if (!isalnum((unsigned char)*p) && *p != '-' && *p != '_')
+            return 0;
+    }
+    return 1;
+}
+
+/*
+ * Include files within dirname.  Only files with names consisting entirely of
+ * alphanumeric chracters, dashes, and underscores are included, in order to
+ * avoid including editor backup files, .rpmsave files, and the like.
+ */
+static errcode_t parse_include_dir(char *dirname, struct parse_state *state)
+{
+    DIR     *dir;
+    char    *pathname;
+    errcode_t retval;
+    struct dirent *ent;
+
+    dir = opendir(dirname);
+    if (dir == NULL)
+        return PROF_FAIL_INCLUDE_DIR;
+    while ((ent = readdir(dir)) != NULL) {
+        if (!valid_name(ent->d_name))
+            continue;
+        if (asprintf(&pathname, "%s/%s", dirname, ent->d_name) < 0)
+            return ENOMEM;
+        retval = parse_include_file(pathname, state);
+        free(pathname);
+        if (retval)
+            return retval;
+    }
+    return 0;
+}
+
 static errcode_t parse_line(char *line, struct parse_state *state)
 {
     char    *cp;
 
+    if (strncmp(line, "include", 7) == 0 && isspace(line[7])) {
+        cp = skip_over_blanks(line + 7);
+        strip_line(cp);
+        return parse_include_file(cp, state);
+    }
+    if (strncmp(line, "includedir", 10) == 0 && isspace(line[10])) {
+        cp = skip_over_blanks(line + 10);
+        strip_line(cp);
+        return parse_include_dir(cp, state);
+    }
     switch (state->state) {
     case STATE_INIT_COMMENT:
         if (line[0] != '[')
@@ -221,29 +290,22 @@
     return 0;
 }
 
-errcode_t profile_parse_file(FILE *f, struct profile_node **root)
+static errcode_t parse_file(FILE *f, struct parse_state *state)
 {
 #define BUF_SIZE        2048
     char *bptr;
     errcode_t retval;
-    struct parse_state state;
 
     bptr = malloc (BUF_SIZE);
     if (!bptr)
         return ENOMEM;
 
-    retval = parse_init_state(&state);
-    if (retval) {
-        free (bptr);
-        return retval;
-    }
     while (!feof(f)) {
         if (fgets(bptr, BUF_SIZE, f) == NULL)
             break;
 #ifndef PROFILE_SUPPORTS_FOREIGN_NEWLINES
-        retval = parse_line(bptr, &state);
+        retval = parse_line(bptr, state);
         if (retval) {
-            profile_free_node(state.root_section);
             free (bptr);
             return retval;
         }
@@ -286,9 +348,8 @@
 
                 /* parse_line modifies contents of p */
                 newp = p + strlen (p) + 1;
-                retval = parse_line (p, &state);
+                retval = parse_line (p, state);
                 if (retval) {
-                    profile_free_node(state.root_section);
                     free (bptr);
                     return retval;
                 }
@@ -298,12 +359,35 @@
         }
 #endif
     }
-    *root = state.root_section;
 
     free (bptr);
     return 0;
 }
 
+errcode_t profile_parse_file(FILE *f, struct profile_node **root)
+{
+    struct parse_state state;
+    errcode_t retval;
+
+    *root = NULL;
+
+    /* Initialize parsing state with a new root node. */
+    state.state = STATE_INIT_COMMENT;
+    state.group_level = 0;
+    state.current_section = NULL;
+    retval = profile_create_node("(root)", 0, &state.root_section);
+    if (retval)
+        return retval;
+
+    retval = parse_file(f, &state);
+    if (retval) {
+        profile_free_node(state.root_section);
+        return retval;
+    }
+    *root = state.root_section;
+    return 0;
+}
+
 /*
  * Return TRUE if the string begins or ends with whitespace
  */

Modified: branches/plugins2/src/util/profile/prof_test1
===================================================================
--- branches/plugins2/src/util/profile/prof_test1	2010-08-26 16:59:37 UTC (rev 24258)
+++ branches/plugins2/src/util/profile/prof_test1	2010-08-26 23:19:40 UTC (rev 24259)
@@ -147,8 +147,97 @@
     puts "OK: test3: Clearing relation and adding one entry yields correct count."
 }
 
+# Exercise the include and includedir directives.
+proc test4 {} {
+    global wd verbose
+
+    # Test expected error message when including nonexistent file.
+    catch [file delete $wd/testinc.ini]
+    exec echo "include does-not-exist" >$wd/testinc.ini
+    catch { profile_init_path $wd/testinc.ini } err
+    if $verbose { puts "Got error message $err" }
+    if { $err ne "Included profile file could not be read" } {
+	puts stderr "Error: test4: Did not get expected error when including nonexistent file."
+	exit 1
+    }
+
+    # Test expected error message when including nonexistent directory.
+    catch [file delete $wd/testinc.ini]
+    exec echo "includedir does-not-exist" >$wd/testinc.ini
+    catch { profile_init_path $wd/testinc.ini } err
+    if $verbose { puts "Got error message $err" }
+    if { $err ne "Included profile directory could not be read" } {
+	puts stderr "Error: test4: Did not get expected error when including nonexistent directory."
+	exit 1
+    }
+
+    # Test including a file.
+    catch [file delete $wd/testinc.ini]
+    exec echo "include $wd/test2.ini" >$wd/testinc.ini
+    set p [profile_init_path $wd/testinc.ini]
+    set x [profile_get_values $p {{test section 1} bar}]
+    if $verbose { puts "Read $x from included profile" }
+    if { [lindex $x 0] ne "foo" } {
+	puts stderr "Error: test4: Did not get expected result from included profile."
+	exit 1
+    }
+    profile_release $p
+
+    # Test including a directory.  (Put two copies of test2.ini inside
+    # it and check that we get two values for one of the variables.)
+    catch [file delete -force $wd/test_include_dir]
+    exec mkdir $wd/test_include_dir
+    exec cp $wd/test2.ini $wd/test_include_dir/a
+    exec cp $wd/test2.ini $wd/test_include_dir/b
+    catch [file delete $wd/testinc.ini]
+    exec echo "includedir $wd/test_include_dir" >$wd/testinc.ini
+    set p [profile_init_path $wd/testinc.ini]
+    set x [profile_get_values $p {{test section 1} bar}]
+    if $verbose { puts "Read $x from included directory" }
+    if { $x ne "foo foo" } {
+	puts stderr, "Error: test4: Did not get expected result from included directory."
+	exit 1
+    }
+    profile_release $p
+
+    puts "OK: test4: include and includedir directives"
+}
+
+proc test5 {} {
+    global wd verbose
+
+    # Test syntactic independence of included profile files.
+    catch [file delete $wd/testinc.ini]
+    set f [open "$wd/testinc.ini" w]
+    puts $f {[sec1]}
+    puts $f "var = {"
+    puts $f "a = 1"
+    puts $f "include testinc2.ini"
+    puts $f "c = 3"
+    puts $f "}"
+    close $f
+    catch [file delete $wd/testinc2.ini]
+    set f [open "$wd/testinc2.ini" w]
+    puts $f {[sec2]}
+    puts $f "b = 2"
+    close $f
+    set p [profile_init_path $wd/testinc.ini]
+    set a [profile_get_values $p {sec1 var a}]
+    set b [profile_get_values $p {sec2 b}]
+    set c [profile_get_values $p {sec1 var c}]
+    if $verbose { puts "Read values [concat $a $b $c] from profile" }
+    if { $a != 1 || $b != 2 || $c != 3 } {
+	puts stderr, "Error: test5: Wrong results from profile"
+	exit 1
+    }
+
+    puts "OK: test5: syntax independence of included files"
+}
+
 test1
 test2
 test3
+test4
+test5
 
 exit 0

Property changes on: branches/plugins2/src/util/profile
___________________________________________________________________



More information about the cvs-krb5 mailing list