changeset 13144:9615cdbf7b70

PSARC 2010/135 Kerberos Diagnostic Enhancements (umbrella case) 6835328 Error messages generated by applications using RPCSEC_GSS are too vague
author Glenn Barry <Glenn.Barry@oracle.com>
date Mon, 16 Aug 2010 17:01:32 -0700
parents 87d7bfd32811
children 83213fd85699
files usr/src/cmd/gss/gssd/gssd_proc.c usr/src/lib/gss_mechs/mech_krb5/Makefile.com usr/src/lib/gss_mechs/mech_krb5/crypto/make_random_key.c usr/src/lib/gss_mechs/mech_krb5/et/krb5_err.c usr/src/lib/gss_mechs/mech_krb5/krb5/ccache/cc_file.c usr/src/lib/gss_mechs/mech_krb5/krb5/keytab/kt_file.c usr/src/lib/gss_mechs/mech_krb5/krb5/krb/fwd_tgt.c usr/src/lib/gss_mechs/mech_krb5/krb5/krb/gc_frm_kdc.c usr/src/lib/gss_mechs/mech_krb5/krb5/krb/gc_via_tkt.c usr/src/lib/gss_mechs/mech_krb5/krb5/krb/get_in_tkt.c usr/src/lib/gss_mechs/mech_krb5/krb5/krb/gic_pwd.c usr/src/lib/gss_mechs/mech_krb5/krb5/krb/int-proto.h usr/src/lib/gss_mechs/mech_krb5/krb5/krb/pac.c usr/src/lib/gss_mechs/mech_krb5/krb5/krb/rd_cred.c usr/src/lib/gss_mechs/mech_krb5/krb5/krb/rd_priv.c usr/src/lib/gss_mechs/mech_krb5/krb5/krb/rd_req_dec.c usr/src/lib/gss_mechs/mech_krb5/krb5/krb/send_tgs.c usr/src/lib/gss_mechs/mech_krb5/krb5/krb/valid_times.c usr/src/lib/gss_mechs/mech_krb5/krb5/krb/walk_rtree.c usr/src/lib/gss_mechs/mech_krb5/krb5/os/hostaddr.c usr/src/lib/gss_mechs/mech_krb5/krb5/os/locate_kdc.c usr/src/lib/gss_mechs/mech_krb5/krb5/os/sendto_kdc.c usr/src/lib/gss_mechs/mech_krb5/krb5/os/sn2princ.c usr/src/lib/gss_mechs/mech_krb5/krb5/rcache/rc_io.c usr/src/lib/gss_mechs/mech_krb5/mapfile-vers usr/src/lib/gss_mechs/mech_krb5/mech/accept_sec_context.c usr/src/lib/gss_mechs/mech_krb5/mech/acquire_cred.c usr/src/lib/gss_mechs/mech_krb5/mech/add_cred.c usr/src/lib/gss_mechs/mech_krb5/mech/context_time.c usr/src/lib/gss_mechs/mech_krb5/mech/copy_ccache.c usr/src/lib/gss_mechs/mech_krb5/mech/disp_com_err_status.c usr/src/lib/gss_mechs/mech_krb5/mech/disp_name.c usr/src/lib/gss_mechs/mech_krb5/mech/disp_status.c usr/src/lib/gss_mechs/mech_krb5/mech/errmap.h usr/src/lib/gss_mechs/mech_krb5/mech/error_map.h usr/src/lib/gss_mechs/mech_krb5/mech/export_name.c usr/src/lib/gss_mechs/mech_krb5/mech/export_sec_context.c usr/src/lib/gss_mechs/mech_krb5/mech/gss_libinit.c usr/src/lib/gss_mechs/mech_krb5/mech/import_name.c usr/src/lib/gss_mechs/mech_krb5/mech/init_sec_context.c usr/src/lib/gss_mechs/mech_krb5/mech/krb5_gss_glue.c usr/src/lib/gss_mechs/mech_krb5/mech/rel_cred.c usr/src/lib/gss_mechs/mech_krb5/mech/util_errmap.c usr/src/lib/gss_mechs/mech_spnego/Makefile.com usr/src/lib/gss_mechs/mech_spnego/mech/gssapiP_spnego.h usr/src/lib/gss_mechs/mech_spnego/mech/spnego_disp_status.c usr/src/lib/gss_mechs/mech_spnego/mech/spnego_kerrs.c usr/src/lib/gss_mechs/mech_spnego/mech/spnego_mech.c usr/src/lib/libgss/Makefile.com usr/src/lib/libgss/g_accept_sec_context.c usr/src/lib/libgss/g_acquire_cred.c usr/src/lib/libgss/g_canon_name.c usr/src/lib/libgss/g_compare_name.c usr/src/lib/libgss/g_context_time.c usr/src/lib/libgss/g_delete_sec_context.c usr/src/lib/libgss/g_dsp_name.c usr/src/lib/libgss/g_dsp_status.c usr/src/lib/libgss/g_dup_name.c usr/src/lib/libgss/g_exp_sec_context.c usr/src/lib/libgss/g_glue.c usr/src/lib/libgss/g_imp_name.c usr/src/lib/libgss/g_imp_sec_context.c usr/src/lib/libgss/g_init_sec_context.c usr/src/lib/libgss/g_initialize.c usr/src/lib/libgss/g_inq_context_oid.c usr/src/lib/libgss/g_inquire_context.c usr/src/lib/libgss/g_inquire_cred.c usr/src/lib/libgss/g_inquire_names.c usr/src/lib/libgss/g_oid_ops.c usr/src/lib/libgss/g_process_context.c usr/src/lib/libgss/g_rel_cred.c usr/src/lib/libgss/g_seal.c usr/src/lib/libgss/g_sign.c usr/src/lib/libgss/g_store_cred.c usr/src/lib/libgss/g_unseal.c usr/src/lib/libgss/g_verify.c usr/src/uts/common/gssapi/include/mechglueP.h usr/src/uts/common/gssapi/mechs/krb5/include/gssapiP_generic.h usr/src/uts/common/gssapi/mechs/krb5/include/gssapiP_krb5.h usr/src/uts/common/gssapi/mechs/krb5/include/k5-int.h usr/src/uts/common/gssapi/mechs/krb5/include/k5-thread.h usr/src/uts/common/gssapi/mechs/krb5/include/krb5.h usr/src/uts/common/gssapi/mechs/krb5/mech/delete_sec_context.c usr/src/uts/common/gssapi/mechs/krb5/mech/gssapi_krb5.c usr/src/uts/common/gssapi/mechs/krb5/mech/import_sec_context.c usr/src/uts/common/gssapi/mechs/krb5/mech/k5seal.c usr/src/uts/common/gssapi/mechs/krb5/mech/k5sealv3.c usr/src/uts/common/gssapi/mechs/krb5/mech/k5unseal.c usr/src/uts/common/gssapi/mechs/krb5/mech/val_cred.c
diffstat 89 files changed, 3906 insertions(+), 1046 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/cmd/gss/gssd/gssd_proc.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/cmd/gss/gssd/gssd_proc.c	Mon Aug 16 17:01:32 2010 -0700
@@ -1,3 +1,6 @@
+/*
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
+ */
 /*
  * CDDL HEADER START
  *
@@ -18,10 +21,6 @@
  *
  * CDDL HEADER END
  */
-/*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
- */
 
 /*
  *  RPC server procedures for the gssapi usermode daemon gssd.
@@ -35,7 +34,6 @@
 #include <strings.h>
 #include <limits.h>
 #include <sys/param.h>
-#include <sys/syslog.h>
 #include <mechglueP.h>
 #include "gssd.h"
 #include <gssapi/gssapi.h>
@@ -72,6 +70,60 @@
 extern void set_gssd_uid(uid_t);
 extern int __rpc_get_local_uid(SVCXPRT *, uid_t *);
 
+/*
+ * Syslog (and output to stderr if debug set) the GSSAPI major
+ * and minor numbers.
+ */
+static void
+syslog_gss_error(OM_uint32 maj_stat, OM_uint32 min_stat, char *errstr)
+{
+	OM_uint32 gmaj_stat, gmin_stat;
+	gss_buffer_desc msg;
+	OM_uint32 msg_ctx = 0;
+
+
+	if (gssd_debug)
+		fprintf(stderr,
+		    "gssd: syslog_gss_err: called from %s: maj=%d min=%d\n",
+		    errstr ? errstr : "<null>", maj_stat, min_stat);
+
+	/* Print the major status error from the mech. */
+	/* msg_ctx - skip the check for it as is probably unnecesary */
+	gmaj_stat = gss_display_status(&gmin_stat, maj_stat,
+	    GSS_C_GSS_CODE,
+	    GSS_C_NULL_OID, &msg_ctx, &msg);
+	if ((gmaj_stat == GSS_S_COMPLETE)||
+	    (gmaj_stat == GSS_S_CONTINUE_NEEDED)) {
+		syslog(LOG_DAEMON|LOG_NOTICE,
+		    "GSSAPI error major: %s", (char *)msg.value);
+		if (gssd_debug)
+			(void) fprintf(stderr,
+			    "gssd: GSSAPI error major: %s\n",
+			    (char *)msg.value);
+
+		(void) gss_release_buffer(&gmin_stat, &msg);
+	}
+
+	/* Print the minor status error from the mech. */
+	msg_ctx = 0;
+	/* msg_ctx - skip the check for it as is probably unnecesary */
+	gmaj_stat = gss_display_status(&gmin_stat, min_stat,
+	    GSS_C_MECH_CODE,
+	    GSS_C_NULL_OID,
+	    &msg_ctx, &msg);
+	if ((gmaj_stat == GSS_S_COMPLETE)||
+	    (gmaj_stat == GSS_S_CONTINUE_NEEDED)) {
+		syslog(LOG_DAEMON|LOG_NOTICE,
+		    "GSSAPI error minor: %s",
+		    (char *)msg.value);
+		if (gssd_debug)
+			(void) fprintf(stderr,
+			    "gssd: GSSAPI error minor: %s\n",
+			    (char *)msg.value);
+		(void) gss_release_buffer(&gmin_stat, &msg);
+	}
+}
+
 void
 gssd_setup(char *arg)
 {
@@ -490,7 +542,9 @@
 			gssd_time_verf = (OM_uint32)time(NULL);
 		}
 		res->gssd_cred_verifier = gssd_time_verf;
-	}
+	} else
+		syslog_gss_error(res->status, res->minor_status,
+		    "acquire_cred");
 
 	/*
 	 * now release the space allocated by the underlying gssapi mechanism
@@ -642,10 +696,9 @@
 				&res->acceptor_time_rec);
 
 	if ((res->status != GSS_S_COMPLETE) &&
-		(res->status != GSS_S_DUPLICATE_ELEMENT) &&
-		(gssd_debug))
-		fprintf(stderr, gettext("gss_add_cred failed status %d \n"),
-			res->status);
+		(res->status != GSS_S_DUPLICATE_ELEMENT))
+		syslog_gss_error(res->status, res->minor_status, "add_cred");
+
 	/*
 	 * convert the output args from the parameter given in the call to the
 	 * variable in the XDR result
@@ -1014,6 +1067,8 @@
 		} else
 			res->actual_mech_type.GSS_OID_len = 0;
 	} else {
+		syslog_gss_error(res->status, res->minor_status,
+			    "init_sec_context");
 		if (context_handle != GSS_C_NO_CONTEXT) {
 			(void) gss_delete_sec_context(&minor_status,
 				&context_handle, NULL);
@@ -1306,6 +1361,9 @@
 			res->mech_type.GSS_OID_len = 0;
 		}
 	} else {
+		syslog_gss_error(res->status, res->minor_status,
+			    "accept_sec_context");
+
 		if (context_handle != GSS_C_NO_CONTEXT) {
 			(void) gss_delete_sec_context(&minor_status,
 				&context_handle, NULL);
@@ -1377,6 +1435,9 @@
 				context_handle,
 				&token_buffer);
 
+	if (GSS_ERROR(res->status))
+		syslog_gss_error(res->status, res->minor_status,
+			    "process_context_token");
 
 	/* return to caller */
 
@@ -1773,7 +1834,8 @@
 	if (res->status == GSS_S_COMPLETE) {
 		res->msg_token.GSS_BUFFER_T_len = (uint_t)msg_token.length;
 		res->msg_token.GSS_BUFFER_T_val = (char *)msg_token.value;
-	}
+	} else
+		syslog_gss_error(res->status, res->minor_status, "sign");
 
 	/* return to caller */
 
@@ -1837,8 +1899,10 @@
 				&token_buffer,
 				&res->qop_state);
 
+	if (GSS_ERROR(res->status))
+		syslog_gss_error(res->status, res->minor_status, "verify");
+
 	/* return to caller */
-
 	return (TRUE);
 }
 
@@ -1918,7 +1982,8 @@
 				(uint_t)output_message_buffer.length;
 		res->output_message_buffer.GSS_BUFFER_T_val =
 				(char *)output_message_buffer.value;
-	}
+	} else
+		syslog_gss_error(res->status, res->minor_status, "seal");
 
 /* return to caller */
 
@@ -1998,7 +2063,8 @@
 				(uint_t)output_message_buffer.length;
 		res->output_message_buffer.GSS_BUFFER_T_val =
 				(char *)output_message_buffer.value;
-	}
+	} else
+		syslog_gss_error(res->status, res->minor_status, "unseal");
 
 
 	/* return to caller */
@@ -2205,8 +2271,11 @@
 					&res->cred_usage,
 					&mechanisms);
 
-	if (res->status != GSS_S_COMPLETE)
+	if (res->status != GSS_S_COMPLETE) {
+		syslog_gss_error(res->status, res->minor_status,
+				"inquire_cred");
 		return (TRUE);
+	}
 
 	/* convert the returned name from internal to external format */
 
--- a/usr/src/lib/gss_mechs/mech_krb5/Makefile.com	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/gss_mechs/mech_krb5/Makefile.com	Mon Aug 16 17:01:32 2010 -0700
@@ -171,7 +171,7 @@
 	util_cksum.o acquire_cred.o init_sec_context.o \
 	set_ccache.o acquire_cred_with_pw.o lucid_context.o \
 	set_allowable_enctypes.o oid_ops.o export_name.o gss_libinit.o \
-	util_buffer_set.o
+	util_buffer_set.o util_errmap.o
 
 MECH_UTS= delete_sec_context.o gssapi_krb5.o \
 	import_sec_context.o k5seal.o k5sealv3.o \
--- a/usr/src/lib/gss_mechs/mech_krb5/crypto/make_random_key.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/gss_mechs/mech_krb5/crypto/make_random_key.c	Mon Aug 16 17:01:32 2010 -0700
@@ -1,6 +1,5 @@
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 
@@ -32,6 +31,7 @@
 
 #include "k5-int.h"
 #include "etypes.h"
+#include <locale.h>
 
 krb5_error_code KRB5_CALLCONV
 krb5_c_make_random_key(krb5_context context, krb5_enctype enctype,
@@ -52,7 +52,9 @@
     /* Solaris Kerberos: Better error message */
     if (i == krb5_enctypes_length) {
 	krb5_set_error_message(context, KRB5_BAD_ENCTYPE,
-	    "Unknown encryption type: %d", enctype);
+			    dgettext(TEXT_DOMAIN,
+				    "Unknown encryption type: %d"),
+			    enctype);
 	return(KRB5_BAD_ENCTYPE);
     }
 
--- a/usr/src/lib/gss_mechs/mech_krb5/et/krb5_err.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/gss_mechs/mech_krb5/et/krb5_err.c	Mon Aug 16 17:01:32 2010 -0700
@@ -1,6 +1,5 @@
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #include <locale.h>
@@ -94,10 +93,10 @@
 			"Requested server and ticket don't match"));
 	case 27:
 		return (dgettext(TEXT_DOMAIN,
-			"KRB5 error code 27"));
+			"Server principal valid for user2user only"));
 	case 28:
 		return (dgettext(TEXT_DOMAIN,
-			"KRB5 error code 28"));
+			"KDC policy rejects transited path"));
 	case 29:
 		return (dgettext(TEXT_DOMAIN,
 			"A service is not available that is required to "
@@ -215,13 +214,13 @@
 			"Certificate mismatch"));
 	case 67:
 		return (dgettext(TEXT_DOMAIN,
-			"KRB5 error code 67"));
+			"No ticket granting ticket"));
 	case 68:
 		return (dgettext(TEXT_DOMAIN,
-			"KRB5 error code 68"));
+			"Realm not local to KDC"));
 	case 69:
 		return (dgettext(TEXT_DOMAIN,
-			"KRB5 error code 69"));
+			"User to user required"));
 	case 70:
 		return (dgettext(TEXT_DOMAIN,
 			"Can't verify certificate"));
--- a/usr/src/lib/gss_mechs/mech_krb5/krb5/ccache/cc_file.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/gss_mechs/mech_krb5/krb5/ccache/cc_file.c	Mon Aug 16 17:01:32 2010 -0700
@@ -82,7 +82,7 @@
 #include "k5-int.h"
 #include <syslog.h>	/* Solaris Kerberos */
 #include <ctype.h>
-
+#include <locale.h>
 
 #include <stdio.h>
 #include <errno.h>
@@ -2548,8 +2548,9 @@
     default:
 	retval = KRB5_CC_IO;		/* XXX */
 	krb5_set_error_message(context, retval,
-			       "Credentials cache I/O operation failed (%s)",
-			       strerror(errnum));
+			    dgettext(TEXT_DOMAIN,
+				"Credentials cache I/O operation failed (%s)"),
+			    strerror(errnum));
     }
     return retval;
 }
--- a/usr/src/lib/gss_mechs/mech_krb5/krb5/keytab/kt_file.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/gss_mechs/mech_krb5/krb5/keytab/kt_file.c	Mon Aug 16 17:01:32 2010 -0700
@@ -1,6 +1,5 @@
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
@@ -32,6 +31,8 @@
 
 #include "k5-int.h"
 #include <stdio.h>
+#include <locale.h>
+#include <syslog.h>
 
 /*
  * Information needed by internal routines of the file-based ticket
@@ -1097,10 +1098,25 @@
 	    errno = 0;
 	    KTFILEP(id) = fopen(KTFILENAME(id), fopen_mode_rbplus);
 	    if (!KTFILEP(id))
-		return errno ? errno : EMFILE;
+		goto report_errno;
 	    writevno = 1;
-	} else				/* some other error */
-	    return errno ? errno : EMFILE;
+	} else {
+        report_errno:
+            switch (errno) {
+            case 0:
+                /* XXX */
+                return EMFILE;
+            case ENOENT:
+                krb5_set_error_message(context, ENOENT,
+				       /* Solaris Kerberos - added dgettext */
+                                       dgettext(TEXT_DOMAIN,
+					   "Key table file '%s' not found"),
+                                       KTFILENAME(id));
+                return ENOENT;
+            default:
+                return errno;
+            }
+        }
     }
     if ((kerror = krb5_lock_file(context, fileno(KTFILEP(id)), mode))) {
 	(void) fclose(KTFILEP(id));
--- a/usr/src/lib/gss_mechs/mech_krb5/krb5/krb/fwd_tgt.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/gss_mechs/mech_krb5/krb5/krb/fwd_tgt.c	Mon Aug 16 17:01:32 2010 -0700
@@ -1,9 +1,6 @@
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
-
-
 /*
  * lib/krb5/krb/get_in_tkt.c
  *
@@ -34,6 +31,7 @@
 #ifdef HAVE_MEMORY_H
 #include <memory.h>
 #endif
+#include <locale.h>
 
 /* helper function: convert flags to necessary KDC options */
 #define flags2options(flags) (flags & KDC_TKT_COMMON_MASK)
@@ -123,7 +121,22 @@
 	goto errout;
 
     /* tgt->client must be equal to creds.client */
-    if (!krb5_principal_compare(context, tgt.client, creds.client)) {
+    if (!krb5_principal_compare(context, tgt.client, creds.client)) {	
+        /* Solaris Kerberos */
+        char *r_name = NULL;
+	char *t_name = NULL;
+	krb5_error_code r_err, t_err;
+	t_err = krb5_unparse_name(context, tgt.client, &t_name);
+	r_err = krb5_unparse_name(context, creds.client, &r_name);
+	krb5_set_error_message(context, KRB5_PRINC_NOMATCH,
+			    dgettext(TEXT_DOMAIN,
+				    "Requested principal and ticket don't match:  Requested principal is '%s' and TGT principal is '%s'"),
+			    r_err ? "unknown" : r_name,
+			    t_err ? "unknown" : t_name);
+	if (r_name)
+	    krb5_free_unparsed_name(context, r_name);
+	if (t_name)
+	    krb5_free_unparsed_name(context, t_name);
 	retval = KRB5_PRINC_NOMATCH;
 	goto errout;
     }
--- a/usr/src/lib/gss_mechs/mech_krb5/krb5/krb/gc_frm_kdc.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/gss_mechs/mech_krb5/krb5/krb/gc_frm_kdc.c	Mon Aug 16 17:01:32 2010 -0700
@@ -1,9 +1,6 @@
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
-
-
 /*
  * Copyright (c) 1994,2003,2005 by the Massachusetts Institute of Technology.
  * Copyright (c) 1994 CyberSAFE Corporation
@@ -40,6 +37,7 @@
 #include "k5-int.h"
 #include <stdio.h>
 #include "int-proto.h"
+#include <locale.h>
 
 struct tr_state;
 
@@ -145,12 +143,6 @@
 #define HARD_CC_ERR(r) ((r) && (r) != KRB5_CC_NOTFOUND &&	\
 	(r) != KRB5_CC_NOT_KTYPE)
 
-#define IS_TGS_PRINC(c, p)				\
-    ((krb5_princ_size((c), (p)) == 2) &&		\
-     (krb5_princ_component((c), (p), 0)->length ==	\
-      KRB5_TGS_NAME_SIZE) &&				\
-     (!memcmp(krb5_princ_component((c), (p), 0)->data,	\
-	      KRB5_TGS_NAME, KRB5_TGS_NAME_SIZE)))
 
 /*
  * Flags for ccache lookups of cross-realm TGTs.
@@ -475,9 +467,22 @@
    * fetched TGT in ts->kdc_tgts. See changes in try_kdc()
    */
   /*  assert(ts->nxt_tgt == ts->kdc_tgts[ts->ntgts-1]); */
-    if (krb5_princ_size(ts->ctx, ts->nxt_tgt->server) != 2)
+    if (krb5_princ_size(ts->ctx, ts->nxt_tgt->server) != 2) {
+	/* Solaris Kerberos */
+	char *s_name = NULL;
+	int err = krb5_unparse_name(ts->ctx, ts->nxt_tgt->server, &s_name);
+	if (!err) {
+	    krb5_set_error_message(ts->ctx, KRB5_KDCREP_MODIFIED,
+				dgettext(TEXT_DOMAIN,
+					"KDC reply did not match expectations: server '%s' principal size should be 2"),
+				s_name);
+	    krb5_free_unparsed_name(ts->ctx, s_name);
+	} else
+	    krb5_set_error_message(ts->ctx, KRB5_KDCREP_MODIFIED,
+				dgettext(TEXT_DOMAIN,
+					"KDC reply did not match expectations: server principal size should be 2"));
 	return KRB5_KDCREP_MODIFIED;
-
+    }
     r1 = krb5_princ_component(ts->ctx, ts->nxt_tgt->server, 1);
 
     for (kdcptr = ts->cur_kdc + 1; *kdcptr != NULL; kdcptr++) {
@@ -507,6 +512,9 @@
 	    ts->kdc_tgts[ts->ntgts] = NULL;
 	}
 	TR_DBG_RET(ts, "find_nxt_kdc", KRB5_KDCREP_MODIFIED);
+	krb5_set_error_message(ts->ctx, KRB5_KDCREP_MODIFIED,
+			    dgettext(TEXT_DOMAIN,
+				    "KDC reply did not match expectation: KDC not found.  Probably got an unexpected realm referral"));
 	return KRB5_KDCREP_MODIFIED;
     }
     ts->nxt_kdc = kdcptr;
@@ -1121,6 +1129,22 @@
 	     * in a <type>/<host> format that
 	     * krb5_get_fallback_host_realm can deal with.
 	     */
+	    /* Solaris Kerberos */
+	    char *s_name = NULL;
+	    char *c_name = NULL;
+	    krb5_error_code s_err, c_err;
+	    s_err = krb5_unparse_name(context, server, &s_name);
+	    c_err = krb5_unparse_name(context, client, &c_name);
+	    krb5_set_error_message(context, KRB5_ERR_HOST_REALM_UNKNOWN,
+				dgettext(TEXT_DOMAIN,
+					"Cannot determine realm for host: Referral specified but no fallback realm available. Client is '%s' and Server is '%s'"),
+				c_err ? "unknown" : c_name,
+				s_err ? "unknown" : s_name);
+	    if (s_name)
+		krb5_free_unparsed_name(context, s_name);
+	    if (c_name)
+		krb5_free_unparsed_name(context, c_name);
+
 	    DPRINTF(("gc_from_kdc: referral specified "
 		     "but no fallback realm avaiable!\n"));
 	    return KRB5_ERR_HOST_REALM_UNKNOWN;
--- a/usr/src/lib/gss_mechs/mech_krb5/krb5/krb/gc_via_tkt.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/gss_mechs/mech_krb5/krb5/krb/gc_via_tkt.c	Mon Aug 16 17:01:32 2010 -0700
@@ -1,3 +1,6 @@
+/*
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
+ */
 /*
  * lib/krb5/krb/gc_via_tgt.c
  *
@@ -30,15 +33,8 @@
 
 #include "k5-int.h"
 #include "int-proto.h"
-
-#define in_clock_skew(date, now) (labs((date)-(now)) < context->clockskew)
-
-#define IS_TGS_PRINC(c, p)				\
-    ((krb5_princ_size((c), (p)) == 2) &&		\
-     (krb5_princ_component((c), (p), 0)->length ==	\
-      KRB5_TGS_NAME_SIZE) &&				\
-     (!memcmp(krb5_princ_component((c), (p), 0)->data,	\
-	      KRB5_TGS_NAME, KRB5_TGS_NAME_SIZE)))
+#include <locale.h>
+#include <ctype.h>
 
 static krb5_error_code
 krb5_kdcrep2creds(krb5_context context, krb5_kdc_rep *pkdcrep, krb5_address *const *address, krb5_data *psectkt, krb5_creds **ppcreds)
@@ -166,6 +162,7 @@
     krb5_error *err_reply;
     krb5_response tgsrep;
     krb5_enctype *enctypes = 0;
+    char *hostname_used = NULL;
 
 #ifdef DEBUG_REFERRALS
     printf("krb5_get_cred_via_tkt starting; referral flag is %s\n", kdcoptions&KDC_OPT_CANONICALIZE?"on":"off");
@@ -174,8 +171,24 @@
 #endif
 
     /* tkt->client must be equal to in_cred->client */
-    if (!krb5_principal_compare(context, tkt->client, in_cred->client))
+    if (!krb5_principal_compare(context, tkt->client, in_cred->client)) {
+        /* Solaris Kerberos */
+        char *r_name = NULL;
+	char *t_name = NULL;
+	krb5_error_code r_err, t_err;
+	t_err = krb5_unparse_name(context, tkt->client, &t_name);
+	r_err = krb5_unparse_name(context, in_cred->client, &r_name);
+	krb5_set_error_message(context, KRB5_PRINC_NOMATCH,
+			    dgettext(TEXT_DOMAIN,
+				    "Requested principal and ticket don't match:  Requested principal is '%s' and ticket is '%s'"),
+			    r_err ? "unknown" : r_name,
+			    t_err ? "unknown" : t_name);
+	if (r_name)
+	    krb5_free_unparsed_name(context, r_name);
+	if (t_name)
+	    krb5_free_unparsed_name(context, t_name);
 	return KRB5_PRINC_NOMATCH;
+    }
 
     if (!tkt->ticket.length)
 	return KRB5_NO_TKT_SUPPLIED;
@@ -212,12 +225,12 @@
 	enctypes[1] = 0;
     }
     
-    retval = krb5_send_tgs(context, kdcoptions, &in_cred->times, enctypes, 
+    retval = krb5_send_tgs2(context, kdcoptions, &in_cred->times, enctypes, 
 			   in_cred->server, address, in_cred->authdata,
 			   0,		/* no padata */
 			   (kdcoptions & KDC_OPT_ENC_TKT_IN_SKEY) ? 
 			   &in_cred->second_ticket : NULL,
-			   tkt, &tgsrep);
+			    tkt, &tgsrep, &hostname_used);
     if (enctypes)
 	free(enctypes);
     if (retval) {
@@ -249,9 +262,101 @@
 	    switch (err_reply->error) {
 	    case KRB_ERR_GENERIC:
 		krb5_set_error_message(context, retval,
-				       "KDC returned error string: %s",
-				       err_reply->text.data);
+				    /* Solaris Kerberos - added dgettext */
+				    dgettext(TEXT_DOMAIN,
+					    "KDC returned error string: %s"),
+				    err_reply->text.data);
 		break;
+            case KDC_ERR_S_PRINCIPAL_UNKNOWN:
+                {
+                    char *s_name;
+                    if (krb5_unparse_name(context, in_cred->server, &s_name) == 
+0) {
+			/* Solaris Kerberos - added dgettext */
+                        krb5_set_error_message(context, retval,
+                                               dgettext(TEXT_DOMAIN,
+							"Server %s not found in Kerberos database"),
+                                               s_name);
+                        krb5_free_unparsed_name(context, s_name);
+                    } else
+                        /* In case there's a stale S_PRINCIPAL_UNKNOWN
+                           report already noted.  */
+                        krb5_clear_error_message(context);
+                }
+                break;
+
+	    case KRB_AP_ERR_SKEW:
+		/* Solaris Kerberos */
+                {
+                    char *s_name = NULL;
+                    char *c_name = NULL;
+		    char stimestring[17];
+		    char ctimestring[17];
+		    char fill = ' ';
+		    int st_err, ct_err, serr, cerr;
+
+		    st_err = krb5_timestamp_to_sfstring(err_reply->stime,
+							stimestring,
+							sizeof (stimestring),
+							&fill);
+		    ct_err = krb5_timestamp_to_sfstring(err_reply->ctime,
+							ctimestring,
+							sizeof (ctimestring),
+							&fill);
+                    serr = krb5_unparse_name(context, in_cred->server, &s_name);
+                    cerr = krb5_unparse_name(context, in_cred->client, &c_name);
+		    krb5_set_error_message(context, retval,
+					dgettext(TEXT_DOMAIN,
+						"Clock skew too great: '%s' requesting ticket '%s' from KDC '%s' (%s). Skew is %dm."),
+					cerr == 0 ? c_name : "unknown",
+					serr == 0 ? s_name : "unknown",
+					hostname_used ?
+					  hostname_used : "host unknown",
+					st_err == 0 ? stimestring : "unknown",
+					(ct_err||st_err) ? 0 :
+					    abs(err_reply->stime -
+					        err_reply->ctime) / 60);
+
+		    if (s_name)
+			    krb5_free_unparsed_name(context, s_name);
+		    if (c_name)
+			    krb5_free_unparsed_name(context, c_name);
+                }
+	        break;
+	    case KRB_AP_ERR_TKT_NYV:
+		/* Solaris Kerberos */
+                {
+                    char *s_name = NULL;
+                    char *c_name = NULL;
+		    char timestring[17];
+		    char stimestring[17];
+		    char fill = ' ';
+		    krb5_error_code t_err, st_err, cerr, serr;
+
+		    t_err = krb5_timestamp_to_sfstring(tkt->times.starttime,
+						    timestring,
+						    sizeof (timestring),
+						    &fill);
+		    st_err = krb5_timestamp_to_sfstring(err_reply->stime,
+							stimestring,
+							sizeof (stimestring),
+							&fill);
+                    serr = krb5_unparse_name(context, in_cred->server, &s_name);
+                    cerr = krb5_unparse_name(context, in_cred->client, &c_name);
+		    krb5_set_error_message(context, retval,
+					dgettext(TEXT_DOMAIN,
+						"Ticket not yet valid: '%s' requesting ticket '%s' from '%s' (%s). TGT start time is %s"),
+					cerr ? "unknown" : c_name,
+					serr ? "unknown" : s_name,
+					hostname_used ? hostname_used : "host unknown",
+					st_err ? "unknown" : stimestring,
+					t_err ? "unknown" : timestring);
+		    if (s_name)
+		        krb5_free_unparsed_name(context, s_name);
+		    if (c_name)
+		        krb5_free_unparsed_name(context, c_name);
+                }
+	        break;
 	    default:
 #if 0 /* We should stop the KDC from sending back this text, because
 	 if the local language doesn't match the KDC's language, we'd
@@ -264,8 +369,10 @@
 		if (strlen (m) == err_reply->text.length-1
 		    && !strcmp(m, err_reply->text.data))
 		    break;
+		/* Solaris Kerberos - added dgettext */
 		krb5_set_error_message(context, retval,
-				       "%s (KDC supplied additional data: %s)",
+				    dgettext(TEXT_DOMAIN,
+					    "%s (KDC supplied additional data: %s)"),
 				       m, err_reply->text.data);
 #endif
 		break;
@@ -336,6 +443,8 @@
     krb5_free_kdc_rep(context, dec_rep);
 
 error_4:;
+    if (hostname_used)
+        free(hostname_used);
     free(tgsrep.response.data);
 #ifdef DEBUG_REFERRALS
     printf("krb5_get_cred_via_tkt ending; %s\n", retval?error_message(retval):"no error");
--- a/usr/src/lib/gss_mechs/mech_krb5/krb5/krb/get_in_tkt.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/gss_mechs/mech_krb5/krb5/krb/get_in_tkt.c	Mon Aug 16 17:01:32 2010 -0700
@@ -1,9 +1,5 @@
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
- */
-
-
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. */
 /*
  * lib/krb5/krb/get_in_tkt.c
  *
@@ -34,10 +30,12 @@
  */
 
 #include <string.h>
-
+#include <ctype.h>
 #include "k5-int.h"
 #include "int-proto.h"
 #include "os-proto.h"
+#include <locale.h>
+#include <syslog.h>
 
 /*
  All-purpose initial ticket routine, usually called via
@@ -114,11 +112,13 @@
  * unexpected response, an error is returned.
  */
 static krb5_error_code
-send_as_request(krb5_context 		context,
+send_as_request2(krb5_context 		context,
 		krb5_kdc_req		*request,
 		krb5_error ** 		ret_err_reply,
 		krb5_kdc_rep ** 	ret_as_reply,
-		int 			    *use_master)
+		int 			*use_master,
+		char			**hostname_used)
+
 {
     krb5_kdc_rep *as_reply = 0;
     krb5_error_code retval;
@@ -143,9 +143,9 @@
 
     k4_version = packet->data[0];
 send_again:
-    retval = krb5_sendto_kdc(context, packet, 
-			     krb5_princ_realm(context, request->client),
-			     &reply, use_master, tcp_only);
+    retval = krb5_sendto_kdc2(context, packet, 
+			    krb5_princ_realm(context, request->client),
+			    &reply, use_master, tcp_only, hostname_used);
     if (retval)
 	goto cleanup;
 
@@ -167,8 +167,10 @@
 		goto send_again;
 	    }
 	    *ret_err_reply = err_reply;
-	} else
+	} else {
 	    krb5_free_error(context, err_reply);
+	    err_reply = NULL;
+	}
 	goto cleanup;
     }
 
@@ -223,6 +225,21 @@
 }
 
 static krb5_error_code
+send_as_request(krb5_context 		context,
+		krb5_kdc_req		*request,
+		krb5_error ** 		ret_err_reply,
+		krb5_kdc_rep ** 	ret_as_reply,
+		int 			    *use_master)
+{
+	return send_as_request2(context,
+			    request,
+			    ret_err_reply,
+			    ret_as_reply,
+			    use_master,
+			    NULL);
+}
+
+static krb5_error_code
 decrypt_as_reply(krb5_context 		context,
 		 krb5_kdc_req		*request,
 		 krb5_kdc_rep		*as_reply,
@@ -520,9 +537,26 @@
     int			loopcount = 0;
     krb5_int32		do_more = 0;
     int             use_master = 0;
+    char *hostname_used = NULL;
 
-    if (! krb5_realm_compare(context, creds->client, creds->server))
+    if (! krb5_realm_compare(context, creds->client, creds->server)) {
+	/* Solaris Kerberos */
+	char *s_name = NULL;
+	char *c_name = NULL;
+	krb5_error_code serr, cerr;
+	serr = krb5_unparse_name(context, creds->server, &s_name);
+	cerr = krb5_unparse_name(context, creds->client, &c_name);
+	krb5_set_error_message(context, KRB5_IN_TKT_REALM_MISMATCH,
+			    dgettext(TEXT_DOMAIN,
+				    "Client/server realm mismatch in initial ticket request: '%s' requesting ticket '%s'"),
+			    cerr ? "unknown" : c_name,
+			    serr ? "unknown" : s_name);
+	if (s_name)
+	    krb5_free_unparsed_name(context, s_name);
+	if (c_name)
+	    krb5_free_unparsed_name(context, c_name);
 	return KRB5_IN_TKT_REALM_MISMATCH;
+    }
 
     if (ret_as_reply)
 	*ret_as_reply = 0;
@@ -599,6 +633,24 @@
     while (1) {
 	if (loopcount++ > MAX_IN_TKT_LOOPS) {
 	    retval = KRB5_GET_IN_TKT_LOOP;
+	    /* Solaris Kerberos */
+	    {
+                char *s_name = NULL;
+		char *c_name = NULL;
+		krb5_error_code serr, cerr;
+		serr = krb5_unparse_name(context, creds->server, &s_name);
+		cerr = krb5_unparse_name(context, creds->client, &c_name);
+		krb5_set_error_message(context, retval,
+				    dgettext(TEXT_DOMAIN,
+					    "Looping detected getting ticket: '%s' requesting ticket '%s'. Max loops is %d.  Make sure a KDC is available"),
+				    cerr ? "unknown" : c_name,
+				    serr ? "unknown" : s_name,
+				    MAX_IN_TKT_LOOPS);
+		if (s_name)
+		    krb5_free_unparsed_name(context, s_name);
+		if (c_name)
+		    krb5_free_unparsed_name(context, c_name);
+	    }
 	    goto cleanup;
 	}
 
@@ -621,8 +673,9 @@
          */
 	request.nonce = (krb5_int32) time_now;
 
-	if ((retval = send_as_request(context, &request, &err_reply,
-				      &as_reply, &use_master)))
+	if ((retval = send_as_request2(context, &request, &err_reply,
+				    &as_reply, &use_master,
+				    &hostname_used)))
 	    goto cleanup;
 
 	if (err_reply) {
@@ -631,6 +684,7 @@
 		retval = decode_krb5_padata_sequence(&err_reply->e_data,
 						     &preauth_to_use);
 		krb5_free_error(context, err_reply);
+                err_reply = NULL;
 		if (retval)
 		    goto cleanup;
                 retval = sort_krb5_padata_sequence(context,
@@ -643,6 +697,7 @@
 		retval = (krb5_error_code) err_reply->error 
 		    + ERROR_TABLE_BASE_krb5;
 		krb5_free_error(context, err_reply);
+                err_reply = NULL;
 		goto cleanup;
 	    }
 	} else if (!as_reply) {
@@ -690,6 +745,9 @@
 	else
 	    krb5_free_kdc_rep(context, as_reply);
     }
+    if (hostname_used)
+        free(hostname_used);
+
     return (retval);
 }
 
@@ -894,6 +952,24 @@
     return 0;
 }
 
+/*
+ * Solaris Kerberos
+ * Return 1 if any char in string is lower-case.
+ */
+static int
+is_lower_case(char *s)
+{
+    if (!s)
+	return 0;
+
+    while (*s) {
+	if (islower((int)*s))
+	    return 1;
+	s++;
+    }
+    return 0;
+}
+
 krb5_error_code KRB5_CALLCONV
 krb5_get_init_creds(krb5_context context,
 		    krb5_creds *creds,
@@ -920,11 +996,12 @@
     krb5_data salt;
     krb5_data s2kparams;
     krb5_keyblock as_key;
-    krb5_error *err_reply;
+    krb5_error *err_reply = NULL;
     krb5_kdc_rep *local_as_reply;
     krb5_timestamp time_now;
     krb5_enctype etype = 0;
     krb5_preauth_client_rock get_data_rock;
+    char *hostname_used = NULL;
 
     /* initialize everything which will be freed at cleanup */
 
@@ -944,9 +1021,7 @@
 
     (void) memset(&as_key, 0, sizeof(as_key));
 
-	local_as_reply = 0;
-
-    err_reply = NULL;
+    local_as_reply = 0;
 
     /*
      * Set up the basic request structure
@@ -1221,10 +1296,11 @@
 	if (ret)
 	    goto cleanup;
 
-	err_reply = 0;
+	err_reply = NULL;
 	local_as_reply = 0;
-	if ((ret = send_as_request(context, &request, &err_reply,
-				   &local_as_reply, use_master)))
+	if ((ret = send_as_request2(context, &request, &err_reply,
+				    &local_as_reply, use_master,
+				    &hostname_used)))
 	    goto cleanup;
 
 	if (err_reply) {
@@ -1237,8 +1313,8 @@
 		}
 		ret = decode_krb5_padata_sequence(&err_reply->e_data,
 						  &preauth_to_use);
-		krb5_free_error(context, err_reply);
-		err_reply = NULL;
+ 		krb5_free_error(context, err_reply);
+ 		err_reply = NULL;
 		if (ret)
 		    goto cleanup;
 		ret = sort_krb5_padata_sequence(context,
@@ -1254,7 +1330,6 @@
 		    /* error + no hints = give up */
 		    ret = (krb5_error_code) err_reply->error
 		          + ERROR_TABLE_BASE_krb5;
-		    krb5_free_error(context, err_reply);
 		    goto cleanup;
 		}
 	    }
@@ -1268,6 +1343,24 @@
 
     if (loopcount == MAX_IN_TKT_LOOPS) {
 	ret = KRB5_GET_IN_TKT_LOOP;
+	/* Solaris Kerberos */
+	{
+            char *s_name = NULL;
+	    char *c_name = NULL;
+	    krb5_error_code serr, cerr;
+	    serr = krb5_unparse_name(context, creds->server, &s_name);
+	    cerr = krb5_unparse_name(context, creds->client, &c_name);
+	    krb5_set_error_message(context, ret,
+				dgettext(TEXT_DOMAIN,
+					"Looping detected getting initial creds: '%s' requesting ticket '%s'. Max loops is %d.  Make sure a KDC is available"),
+				cerr ? "unknown" : c_name,
+				serr ? "unknown" : s_name,
+				MAX_IN_TKT_LOOPS);
+	    if (s_name)
+		krb5_free_unparsed_name(context, s_name);
+	    if (c_name)
+		krb5_free_unparsed_name(context, c_name);
+	}
 	goto cleanup;
     }
 
@@ -1340,6 +1433,95 @@
     ret = 0;
 
 cleanup:
+    if (ret != 0) {
+        char *client_name = NULL;
+        /* See if we can produce a more detailed error message.  */
+        switch (ret) {
+        case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN:
+            if (krb5_unparse_name(context, client, &client_name) == 0) {
+                krb5_set_error_message(context, ret,
+                                       dgettext(TEXT_DOMAIN,
+						"Client '%s' not found in Kerberos database"),
+                                       client_name);
+                free(client_name);
+            }
+            break;
+        /* Solaris Kerberos: spruce-up the err msg */
+	case KRB5_PREAUTH_FAILED:
+	case KRB5KDC_ERR_PREAUTH_FAILED:
+            if (krb5_unparse_name(context, client, &client_name) == 0) {
+                krb5_set_error_message(context, ret,
+				    dgettext(TEXT_DOMAIN,
+				      "Client '%s' pre-authentication failed"),
+                                       client_name);
+                free(client_name);
+            }
+            break;
+	/* Solaris Kerberos: spruce-up the err msg */
+	case KRB5KRB_AP_ERR_SKEW: /* KRB_AP_ERR_SKEW + ERROR_TABLE_BASE_krb5 */
+	    {
+                char *s_name = NULL;
+		char *c_name = NULL;
+		char stimestring[17];
+		char fill = ' ';
+		krb5_error_code c_err, s_err, s_time;
+
+		s_err = krb5_unparse_name(context,
+					err_reply->server, &s_name);
+		s_time = krb5_timestamp_to_sfstring(err_reply->stime,
+						    stimestring,
+						    sizeof (stimestring),
+						    &fill);
+		c_err = krb5_unparse_name(context, client, &c_name);
+		krb5_set_error_message(context, ret,
+				    dgettext(TEXT_DOMAIN,
+					    "Clock skew too great: '%s' requesting ticket '%s' from KDC '%s' (%s). Skew is %dm"),
+				    c_err == 0 ? c_name : "unknown",
+				    s_err == 0 ? s_name : "unknown",
+				    hostname_used ? hostname_used : "unknown",
+				    s_time == 0 ? stimestring : "unknown",
+				    (s_time != 0) ? 0 :
+				      (abs(err_reply->stime - time_now) / 60));
+		if (s_name)
+			krb5_free_unparsed_name(context, s_name);
+		if (c_name)
+			krb5_free_unparsed_name(context, c_name);
+	    }
+	    break;
+	case KRB5_KDCREP_MODIFIED:
+            if (krb5_unparse_name(context, client, &client_name) == 0) {
+		/*
+		 * Solaris Kerberos
+		 * Extra err msg for common(?) case of 
+		 * 'kinit user@lower-case-def-realm'.
+		 * DNS SRV recs will match (case insensitive) and trigger sendto
+		 * KDC and result in this error (at least w/MSFT AD KDC).
+		 */
+		char *realm = strpbrk(client_name, "@");
+		int set = 0;
+		if (realm++) {
+		    if (realm && realm[0] && is_lower_case(realm)) {
+			krb5_set_error_message(context, ret,
+					    dgettext(TEXT_DOMAIN,
+						    "KDC reply did not match expectations for client '%s': lower-case detected in realm '%s'"),
+					    client_name, realm);
+			set = 1;
+		    }
+		}
+		if (!set)
+		    krb5_set_error_message(context, ret,
+					dgettext(TEXT_DOMAIN,
+						"KDC reply did not match expectations for client '%s'"),                                 
+					client_name);
+                free(client_name);
+            }
+	    break;
+        default:
+            break;
+        }
+    }
+    if (err_reply)
+	    krb5_free_error(context, err_reply);
     krb5_preauth_request_context_fini(context);
     if (encoded_previous_request != NULL) {
 	krb5_free_data(context, encoded_previous_request);
@@ -1374,6 +1556,7 @@
 	*as_reply = local_as_reply;
     else if (local_as_reply)
 	krb5_free_kdc_rep(context, local_as_reply);
-
+    if (hostname_used)
+        free(hostname_used);
     return(ret);
 }
--- a/usr/src/lib/gss_mechs/mech_krb5/krb5/krb/gic_pwd.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/gss_mechs/mech_krb5/krb5/krb/gic_pwd.c	Mon Aug 16 17:01:32 2010 -0700
@@ -1,8 +1,5 @@
 /*
- * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
- */
-
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. */
 
 #include "k5-int.h"
 #include "com_err.h"
@@ -179,6 +176,7 @@
    char admin_realm[1024], *cpw_service=NULL, *princ_str=NULL;
    kadm5_config_params  params;
    void *server_handle;
+   const char *err_msg_1 = NULL;
 
    use_master = 0;
    as_reply = NULL;
@@ -212,7 +210,6 @@
 			     start_time, in_tkt_service, opte,
 			     krb5_get_as_key_password, (void *) &pw0,
 			     &use_master, &as_reply);
-
    /* check for success */
 
    if (ret == 0)
@@ -224,7 +221,7 @@
    if ((ret == KRB5_KDC_UNREACH) ||
        (ret == KRB5_PREAUTH_FAILED) ||
        (ret == KRB5_LIBOS_PWDINTR) ||
-	   (ret == KRB5_REALM_CANT_RESOLVE))
+       (ret == KRB5_REALM_CANT_RESOLVE))
       goto cleanup;
 
    /* if the reply did not come from the master kdc, try again with
@@ -237,6 +234,9 @@
 	  krb5_free_kdc_rep( context, as_reply);
 	  as_reply = NULL;
       }
+      
+      err_msg_1 = krb5_get_error_message(context, ret);
+
       ret2 = krb5_get_init_creds(context, creds, client, prompter, data,
 				 start_time, in_tkt_service, opte,
 				 krb5_get_as_key_password, (void *) &pw0,
@@ -250,12 +250,17 @@
       /* if the master is unreachable, return the error from the
 	 slave we were able to contact or reset the use_master flag */
 
-       if ((ret2 != KRB5_KDC_UNREACH) &&
-	    (ret2 != KRB5_REALM_CANT_RESOLVE) &&
-	    (ret2 != KRB5_REALM_UNKNOWN))
-	   ret = ret2;
-       else
-	   use_master = 0;
+      if ((ret2 != KRB5_KDC_UNREACH) &&
+	(ret2 != KRB5_REALM_CANT_RESOLVE) &&
+	(ret2 != KRB5_REALM_UNKNOWN)) {
+	ret = ret2;
+      } else {
+	use_master = 0;
+	/* Solaris - if 2nd try failed, reset 1st err msg */
+	if (ret2 && err_msg_1) {
+	  krb5_set_error_message(context, ret, err_msg_1);
+	}
+      }
    }
 
 /* Solaris Kerberos: 163 resync */
@@ -409,6 +414,9 @@
 			     &use_master, &as_reply);
 
 cleanup:
+   if (err_msg_1)
+     free((void *)err_msg_1);
+
    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 */
--- a/usr/src/lib/gss_mechs/mech_krb5/krb5/krb/int-proto.h	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/gss_mechs/mech_krb5/krb5/krb/int-proto.h	Mon Aug 16 17:01:32 2010 -0700
@@ -54,5 +54,11 @@
 				 const char *attr,
 				 const char *value);
 
+#define in_clock_skew(date, now) (labs((date)-(now)) < context->clockskew)
+
+#define IS_TGS_PRINC(c, p)                                              \
+    (krb5_princ_size((c), (p)) == 2 &&                                  \
+     data_eq_string(*krb5_princ_component((c), (p), 0), KRB5_TGS_NAME))
+
 #endif /* KRB5_INT_FUNC_PROTO__ */
 
--- a/usr/src/lib/gss_mechs/mech_krb5/krb5/krb/pac.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/gss_mechs/mech_krb5/krb5/krb/pac.c	Mon Aug 16 17:01:32 2010 -0700
@@ -1,6 +1,5 @@
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 /*
  * lib/krb5/krb/pac.c
@@ -189,6 +188,10 @@
 
     /* Check there isn't already a buffer of this type */
     if (k5_pac_locate_buffer(context, pac, type, NULL) == 0) {
+	/* Solaris Kerberos */
+	krb5_set_error_message(context, EINVAL,
+			    "Duplicate PAC buffer of type %d",
+			    type);
 	return EINVAL;
     }
 
@@ -284,20 +287,35 @@
     PAC_INFO_BUFFER *buffer = NULL;
     size_t i;
 
-    if (pac == NULL)
+    if (pac == NULL) {
+	/* Solaris Kerberos */
+	krb5_set_error_message(context, EINVAL,
+			    "Invalid argument 'pac' is NULL");
 	return EINVAL;
+    }
 
     for (i = 0; i < pac->pac->cBuffers; i++) {
 	if (pac->pac->Buffers[i].ulType == type) {
 	    if (buffer == NULL)
 		buffer = &pac->pac->Buffers[i];
-	    else
+	    else {
+	        /* Solaris Kerberos */
+	        krb5_set_error_message(context, EINVAL,
+				    "Invalid buffer found looping thru PAC buffers (type=%d, i=%d)",
+				    type, i);
 		return EINVAL;
+	    }
 	}
     }
 
-    if (buffer == NULL)
+    if (buffer == NULL) {
+	/* Solaris Kerberos */
+	krb5_set_error_message(context, ENOENT,
+			    "No PAC buffer found (type=%d)",
+			    type);
+
 	return ENOENT;
+    }
 
     assert(buffer->Offset + buffer->cbBufferSize <= pac->data.length);
 
@@ -410,20 +428,35 @@
 
     *ppac = NULL;
 
-    if (len < PACTYPE_LENGTH)
+    if (len < PACTYPE_LENGTH) {
+	/* Solaris Kerberos */
+	krb5_set_error_message(context, ERANGE,
+			    "PAC type length is out of range (len=%d)",
+			    len);
 	return ERANGE;
+    }
 
     cbuffers = load_32_le(p);
     p += 4;
     version = load_32_le(p);
     p += 4;
 
-    if (version != 0)
+    if (version != 0) {
+	/* Solaris Kerberos */
+	krb5_set_error_message(context, EINVAL,
+			    "Invalid PAC version is %d, should be 0",
+			    version);
 	return EINVAL;
+    }
 
     header_len = PACTYPE_LENGTH + (cbuffers * PAC_INFO_BUFFER_LENGTH);
-    if (len < header_len)
+    if (len < header_len) {
+	/* Solaris Kerberos */
+	krb5_set_error_message(context, ERANGE,
+			    "PAC header len (%d) out of range",
+			    len);
 	return ERANGE;
+    }
 
     ret = krb5_pac_init(context, &pac);
     if (ret != 0)
@@ -451,11 +484,17 @@
 
 	if (buffer->Offset % PAC_ALIGNMENT) {
 	    krb5_pac_free(context, pac);
+	    /* Solaris Kerberos */
+	    krb5_set_error_message(context, EINVAL,
+				"PAC buffer offset mis-aligned");
 	    return EINVAL;
 	}
 	if (buffer->Offset < header_len ||
 	    buffer->Offset + buffer->cbBufferSize > len) {
 	    krb5_pac_free(context, pac);
+	    /* Solaris Kerberos */
+	    krb5_set_error_message(context, ERANGE,
+				"PAC offset is out of range");
 	    return ERANGE;
 	}
     }
@@ -475,7 +514,7 @@
 }
 
 static krb5_error_code
-k5_time_to_seconds_since_1970(krb5_int64 ntTime, krb5_timestamp *elapsedSeconds)
+k5_time_to_seconds_since_1970(krb5_context context, krb5_int64 ntTime, krb5_timestamp *elapsedSeconds)
 {
     krb5_ui_8 abstime;
 
@@ -483,8 +522,9 @@
 
     abstime = ntTime > 0 ? ntTime - NT_TIME_EPOCH : -ntTime;
 
-    if (abstime > KRB5_INT32_MAX)
+    if (abstime > KRB5_INT32_MAX) {
 	return ERANGE;
+    }
 
     *elapsedSeconds = abstime;
 
@@ -523,8 +563,13 @@
     if (ret != 0)
 	return ret;
 
-    if (client_info.length < PAC_CLIENT_INFO_LENGTH)
+    if (client_info.length < PAC_CLIENT_INFO_LENGTH) {
+	/* Solaris Kerberos */
+	krb5_set_error_message(context, ERANGE,
+			    "PAC client info length out of range",
+			    client_info.length);
 	return ERANGE;
+    }
 
     p = (unsigned char *)client_info.data;
     pac_nt_authtime = load_64_le(p);
@@ -532,13 +577,17 @@
     pac_princname_length = load_16_le(p);
     p += 2;
 
-    ret = k5_time_to_seconds_since_1970(pac_nt_authtime, &pac_authtime);
+    ret = k5_time_to_seconds_since_1970(context, pac_nt_authtime, &pac_authtime);
     if (ret != 0)
 	return ret;
 
     if (client_info.length < PAC_CLIENT_INFO_LENGTH + pac_princname_length ||
-        pac_princname_length % 2)
+        pac_princname_length % 2) {
+	/* Solaris Kerberos */
+	krb5_set_error_message(context, ERANGE,
+			    "PAC client info length is out of range");
 	return ERANGE;
+    }
 
     ret = krb5int_ucs2lecs_to_utf8s(p, (size_t)pac_princname_length / 2, &pac_princname, NULL);
     if (ret != 0)
@@ -550,12 +599,42 @@
 	return ret;
     }
 
-    free(pac_princname);
 
-    if (pac_authtime != authtime ||
-	krb5_principal_compare(context, pac_principal, principal) == FALSE)
+    if (pac_authtime != authtime) {
+	/* Solaris Kerberos */
+	char timestring[17];
+	char pac_timestring[17];
+	char fill = ' ';
+	int err, pac_err;
+	/* Need better ret code here but don't see one */
 	ret = KRB5KRB_AP_WRONG_PRINC;
+	err = krb5_timestamp_to_sfstring(pac_authtime,
+					timestring,
+					sizeof (timestring), &fill);
+	pac_err = krb5_timestamp_to_sfstring(pac_authtime,
+					pac_timestring,
+					    sizeof (pac_timestring), &fill);
+	if (pac_princname && !err && !pac_err) {
+	    krb5_set_error_message(context, ret,
+				"PAC verify fail: PAC authtime '%s' does not match authtime '%s'.  PAC principal is '%s'",
+				pac_timestring, timestring, pac_princname);
+	}
+    } else if (krb5_principal_compare(context, pac_principal, principal) == FALSE) {
+	/* Solaris Kerberos */
+	char *p_name = NULL;
+	krb5_error_code perr;
+	ret = KRB5KRB_AP_WRONG_PRINC;
+	perr = krb5_unparse_name(context, principal, &p_name);
+	if (pac_princname && !perr) {
+	    krb5_set_error_message(context, ret,
+				"Wrong principal in request: PAC verify: Principal in PAC is '%s' and does not match '%s'",
+				pac_princname, p_name);
+	}
+	if (p_name)
+	    krb5_free_unparsed_name(context, p_name);
+    }
 
+    free(pac_princname);
     krb5_free_principal(context, pac_principal);
 
     return ret;
@@ -580,14 +659,21 @@
 	}
     }
 
-    if (buffer == NULL)
+    if (buffer == NULL) {
+	/* Solaris Kerberos */
+	krb5_set_error_message(context, ENOENT,
+			    "No PAC buffer found (type=%d)",
+			    type);
 	return ENOENT;
+    }
 
-    if (buffer->Offset + buffer->cbBufferSize > pac->data.length)
+    if (buffer->Offset + buffer->cbBufferSize > pac->data.length) {
 	return ERANGE;
+    }
 
-    if (buffer->cbBufferSize < PAC_SIGNATURE_DATA_LENGTH)
+    if (buffer->cbBufferSize < PAC_SIGNATURE_DATA_LENGTH) {
 	return KRB5_BAD_MSIZE;
+    }
 
     /* Zero out the data portion of the checksum only */
     memset(data->data + buffer->Offset + PAC_SIGNATURE_DATA_LENGTH,
@@ -613,8 +699,9 @@
     if (ret != 0)
 	return ret;
 
-    if (checksum_data.length < PAC_SIGNATURE_DATA_LENGTH)
+    if (checksum_data.length < PAC_SIGNATURE_DATA_LENGTH) {
 	return KRB5_BAD_MSIZE;
+    }
 
     p = (krb5_octet *)checksum_data.data;
     checksum.checksum_type = load_32_le(p);
@@ -648,8 +735,12 @@
 	return ret;
     }
 
-    if (valid == FALSE)
+    if (valid == FALSE) {
 	ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
+	/* Solaris Kerberos */
+	krb5_set_error_message(context, ret,
+			    "Decrypt integrity check failed for PAC");
+    }
 
     free(pac_data.data); /* SUNW17PACresync - mem leak fix */
     return ret;
@@ -670,15 +761,17 @@
     if (ret != 0)
 	return ret;
 
-    if (privsvr_checksum.length < PAC_SIGNATURE_DATA_LENGTH)
+    if (privsvr_checksum.length < PAC_SIGNATURE_DATA_LENGTH) {
 	return KRB5_BAD_MSIZE;
+    }
 
     ret = k5_pac_locate_buffer(context, pac, PAC_SERVER_CHECKSUM, &server_checksum);
     if (ret != 0)
 	return ret;
 
-    if (server_checksum.length < PAC_SIGNATURE_DATA_LENGTH)
+    if (server_checksum.length < PAC_SIGNATURE_DATA_LENGTH) {
 	return KRB5_BAD_MSIZE;
+    }
 
     p = (krb5_octet *)privsvr_checksum.data;
     checksum.checksum_type = load_32_le(p);
@@ -693,8 +786,12 @@
     if (ret != 0)
 	return ret;
 
-    if (valid == FALSE)
+    if (valid == FALSE) {
 	ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
+	/* Solaris Kerberos */
+	krb5_set_error_message(context, ret,
+			    "Decrypt integrity check failed for PAC");
+    }
 
     return ret;
 }
@@ -709,8 +806,9 @@
 {
     krb5_error_code ret;
 
-    if (server == NULL)
+    if (server == NULL) {
 	return EINVAL;
+    }
 
     ret = k5_pac_verify_server_checksum(context, pac, server);
     if (ret != 0)
@@ -812,8 +910,9 @@
     ret = k5_pac_locate_buffer(context, pac, type, &cksumdata);
     if (ret == 0) {
 	/* If we're resigning PAC, make sure we can fit checksum into existing buffer */
-	if (cksumdata.length != PAC_SIGNATURE_DATA_LENGTH + len)
+	if (cksumdata.length != PAC_SIGNATURE_DATA_LENGTH + len) {
 	    return ERANGE;
+	}
 
 	memset(cksumdata.data, 0, cksumdata.length);
     } else {
@@ -866,8 +965,9 @@
 
 	if (buffer->Offset % PAC_ALIGNMENT ||
 	    buffer->Offset + buffer->cbBufferSize > pac->data.length ||
-	    buffer->Offset < header_len)
+	    buffer->Offset < header_len) {
 	    return ERANGE;
+	}
     }
 
     return 0;
--- a/usr/src/lib/gss_mechs/mech_krb5/krb5/krb/rd_cred.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/gss_mechs/mech_krb5/krb5/krb/rd_cred.c	Mon Aug 16 17:01:32 2010 -0700
@@ -1,9 +1,7 @@
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
-
 #include "k5-int.h"
 #include "cleanup.h"
 #include "auth_con.h"
@@ -167,7 +165,6 @@
 
 /*----------------------- krb5_rd_cred -----------------------*/
 
-#define in_clock_skew(date) (labs((date)-currenttime) < context->clockskew)
 
 /*
  * This functions takes as input an KRB_CRED message, validates it, and
@@ -195,30 +192,25 @@
         return KRB5_RC_REQUIRED;
 
 
-/* If decrypting with the first keyblock we try fails, perhaps the
- * credentials are stored in the session key so try decrypting with
+   /*
+    * If decrypting with the first keyblock we try fails, perhaps the
+    * credentials are stored in the session key so try decrypting with
     * that.
-*/
+    */
     if ((retval = krb5_rd_cred_basic(context, pcreddata, keyblock,
 				     &replaydata, pppcreds))) {
 	if ((retval = krb5_rd_cred_basic(context, pcreddata,
 					 auth_context->keyblock,
 					 &replaydata, pppcreds))) {
 	    return retval;
-    }
+	}
     }
     
     if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) {
         krb5_donot_replay replay;
-        krb5_timestamp currenttime;
 
-        if ((retval = krb5_timeofday(context, &currenttime)))
-            goto error;
-
-        if (!in_clock_skew(replaydata.timestamp)) {
-            retval =  KRB5KRB_AP_ERR_SKEW;
-            goto error;
-        }
+	if ((retval = krb5int_check_clockskew(context, replaydata.timestamp)))
+	    goto error;
 
         if ((retval = krb5_gen_replay_name(context, auth_context->remote_addr,
 					   "_forw", &replay.client)))
--- a/usr/src/lib/gss_mechs/mech_krb5/krb5/krb/rd_priv.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/gss_mechs/mech_krb5/krb5/krb/rd_priv.c	Mon Aug 16 17:01:32 2010 -0700
@@ -1,9 +1,6 @@
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
-
-
 /*
  * lib/krb5/krb/rd_priv.c
  *
@@ -37,7 +34,6 @@
 #include "cleanup.h"
 #include "auth_con.h"
 
-#define in_clock_skew(date) (labs((date)-currenttime) < context->clockskew)
 
 /*
 
@@ -228,16 +224,10 @@
 
     if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) {
 	krb5_donot_replay replay;
-    	krb5_timestamp currenttime;
 
-	if ((retval = krb5_timeofday(context, &currenttime)))
+	if ((retval = krb5int_check_clockskew(context, replaydata.timestamp)))
 	    goto error;
 
-	if (!in_clock_skew(replaydata.timestamp)) {
-	    retval =  KRB5KRB_AP_ERR_SKEW;
-	    goto error;
-	}
-
 	if ((retval = krb5_gen_replay_name(context, auth_context->remote_addr, 
 					   "_priv", &replay.client)))
 	    goto error;
--- a/usr/src/lib/gss_mechs/mech_krb5/krb5/krb/rd_req_dec.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/gss_mechs/mech_krb5/krb5/krb/rd_req_dec.c	Mon Aug 16 17:01:32 2010 -0700
@@ -1,9 +1,6 @@
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
-
-
 /*
  * lib/krb5/krb/rd_req_dec.c
  *
@@ -37,6 +34,8 @@
 
 #include "k5-int.h"
 #include "auth_con.h"
+#include <locale.h>
+#include <syslog.h>
 
 /*
  * essentially the same as krb_rd_req, but uses a decoded AP_REQ as
@@ -69,7 +68,6 @@
 	(krb5_context, const krb5_ap_req *, krb5_authenticator **,
 		   int);
 
-#define in_clock_skew(date) (labs((date)-currenttime) < context->clockskew)
 
 static krb5_error_code
 krb5_rd_req_decrypt_tkt_part(krb5_context context, const krb5_ap_req *req, krb5_keytab keytab)
@@ -77,7 +75,7 @@
     krb5_error_code 	  retval;
     krb5_enctype 	  enctype;
     krb5_keytab_entry 	  ktent;
-
+ 
     enctype = req->ticket->enc_part.enctype;
 
     /* Solaris Kerberos: */
@@ -99,10 +97,51 @@
     retval = krb5_decrypt_tkt_part(context, &ktent.key, req->ticket);
     /* Upon error, Free keytab entry first, then return */
 
+    if (retval == KRB5KRB_AP_ERR_BAD_INTEGRITY) {
+        /* Solaris Kerberos: spruce-up the err msg */
+        krb5_principal princ = (krb5_principal) req->ticket->server;
+	char *s_name = NULL;
+	int kret = krb5_unparse_name(context, princ, &s_name);
+	if (kret == 0) {
+	    krb5_set_error_message(context, retval,
+				dgettext(TEXT_DOMAIN,
+					"AP Request ticket decrypt fail for principal '%s' (kvno=%d, enctype=%d)"),
+				s_name,
+				req->ticket->enc_part.kvno,
+				enctype);
+	   krb5_free_unparsed_name(context, s_name);
+	}
+    }
+	    
     (void) krb5_kt_free_entry(context, &ktent);
     return retval;
 }
 
+/*
+ * Solaris Kerberos
+ * Same as krb5int_check_clockskew() plus return the skew in seconds.
+ */
+static krb5_error_code
+krb5int_check_clockskew2(krb5_context context,
+			krb5_timestamp date,
+			krb5_timestamp *ret_skew)
+{
+    krb5_timestamp currenttime, skew;
+    krb5_error_code retval;
+
+    retval = krb5_timeofday(context, &currenttime);
+    if (retval)
+        return retval;
+
+    skew = labs((date)-currenttime);
+    if (!(skew < context->clockskew)) {
+        *ret_skew = skew;
+        return KRB5KRB_AP_ERR_SKEW;
+    }
+
+    return 0;
+}
+
 static krb5_error_code
 krb5_rd_req_decoded_opt(krb5_context context, krb5_auth_context *auth_context,
 			const krb5_ap_req *req, krb5_const_principal server,
@@ -110,8 +149,8 @@
 			krb5_ticket **ticket, int check_valid_flag)
 {
     krb5_error_code 	  retval = 0;
-    krb5_timestamp 	  currenttime;
     krb5_principal_data princ_data;
+    krb5_timestamp	  skew = 0; /* Solaris Kerberos */
     
     req->ticket->enc_part2 == NULL;
     if (server && krb5_is_referral_realm(&server->realm)) {
@@ -129,7 +168,8 @@
 	if (krb5_unparse_name(context, server, &wanted_name) == 0
 	    && krb5_unparse_name(context, req->ticket->server, &found_name) == 0)
 	    krb5_set_error_message(context, KRB5KRB_AP_WRONG_PRINC,
-				   "Wrong principal in request (found %s, wanted %s)",
+				dgettext(TEXT_DOMAIN,
+					"Wrong principal in request (found %s, wanted %s)"),
 				   found_name, wanted_name);
 	krb5_free_unparsed_name(context, wanted_name);
 	krb5_free_unparsed_name(context, found_name);
@@ -255,6 +295,9 @@
 	    krb5_xfree(rep.client);
 	}
 
+	if (retval == KRB5KRB_AP_ERR_SKEW)
+	    goto err_skew;
+
 	if (retval)
 	    goto cleanup;
     }
@@ -263,19 +306,46 @@
     if (retval != 0)
 	    goto cleanup;
 
-    if ((retval = krb5_timeofday(context, &currenttime)))
-	goto cleanup;
-
-    if (!in_clock_skew((*auth_context)->authentp->ctime)) {
-	retval = KRB5KRB_AP_ERR_SKEW;
-	goto cleanup;
+err_skew:
+    if ((retval = krb5int_check_clockskew2(context,
+					(*auth_context)->authentp->ctime,
+					&skew))) {
+        /* Solaris Kerberos */
+        char *s_name = NULL;
+        char *c_name = NULL;
+	krb5_error_code serr, cerr;
+	serr = krb5_unparse_name(context, req->ticket->server, &s_name);
+	cerr = krb5_unparse_name(context, req->ticket->enc_part2->client,
+				&c_name);
+	krb5_set_error_message(context, retval,
+			    dgettext(TEXT_DOMAIN,
+				    "Clock skew too great: client '%s' AP request with ticket for '%s'. Skew is %dm (allowable %dm)."),
+			    cerr == 0 ? c_name : "unknown",
+			    serr == 0 ? s_name : "unknown",
+			    skew > 0 ? skew/60 : 0,
+			    context->clockskew > 0 ? context->clockskew/60 : 0);
+	if (s_name)
+	    krb5_free_unparsed_name(context, s_name);
+	if (c_name)
+	    krb5_free_unparsed_name(context, c_name);
+        goto cleanup;
     }
 
     if (check_valid_flag) {
-      if (req->ticket->enc_part2->flags & TKT_FLG_INVALID) {
-	retval = KRB5KRB_AP_ERR_TKT_INVALID;
-	goto cleanup;
-      }
+        if (req->ticket->enc_part2->flags & TKT_FLG_INVALID) {
+	    /* Solaris Kerberos */
+	    char *s_name = NULL;
+	    int err = krb5_unparse_name(context, req->ticket->server, &s_name);
+	    retval = KRB5KRB_AP_ERR_TKT_INVALID;
+	    if (!err) {
+	        krb5_set_error_message(context, retval,
+				    dgettext(TEXT_DOMAIN,
+				    "Ticket has invalid flag set for server '%s'"),
+				    s_name);
+	        krb5_free_unparsed_name(context, s_name);
+	    }
+	    goto cleanup;
+	}
     }
 
     /* check if the various etypes are permitted */
@@ -298,8 +368,9 @@
 	    retval = KRB5_NOPERM_ETYPE;
 	    if (krb5_enctype_to_string(etype, enctype_name, sizeof(enctype_name)) == 0)
 		krb5_set_error_message(context, retval,
-				       "Encryption type %s not permitted",
-				       enctype_name);
+				    dgettext(TEXT_DOMAIN,
+					    "Encryption type %s not permitted"),
+				    enctype_name);
 	    goto cleanup;
 	}
     } else {
@@ -316,8 +387,9 @@
 	    if (krb5_enctype_to_string(req->ticket->enc_part.enctype,
 				       enctype_name, sizeof(enctype_name)) == 0)
 		krb5_set_error_message(context, retval,
-				       "Encryption type %s not permitted",
-				       enctype_name);
+				    dgettext(TEXT_DOMAIN,
+					    "Encryption type %s not permitted"),
+				    enctype_name);
 	    goto cleanup;
 	}
 	
@@ -331,8 +403,9 @@
 	    if (krb5_enctype_to_string(req->ticket->enc_part2->session->enctype,
 				       enctype_name, sizeof(enctype_name)) == 0)
 		krb5_set_error_message(context, retval,
-				       "Encryption type %s not permitted",
-				       enctype_name);
+				    dgettext(TEXT_DOMAIN,
+					    "Encryption type %s not permitted"),
+				    enctype_name);
 	    goto cleanup;
 	}
 	
@@ -348,8 +421,9 @@
 					   enctype_name,
 					   sizeof(enctype_name)) == 0)
 		    krb5_set_error_message(context, retval,
-					   "Encryption type %s not permitted",
-					   enctype_name);
+					dgettext(TEXT_DOMAIN,
+					    "Encryption type %s not permitted"),
+					enctype_name);
 		goto cleanup;
 	    }
 	}
--- a/usr/src/lib/gss_mechs/mech_krb5/krb5/krb/send_tgs.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/gss_mechs/mech_krb5/krb5/krb/send_tgs.c	Mon Aug 16 17:01:32 2010 -0700
@@ -137,6 +137,28 @@
 	      krb5_pa_data *const *padata, const krb5_data *second_ticket,
 	      krb5_creds *in_cred, krb5_response *rep)
 {
+	return (krb5_send_tgs2(context, kdcoptions,
+			    timestruct, ktypes,
+			    sname, addrs,
+			    authorization_data,
+			    padata, second_ticket,
+			    in_cred, rep,
+			    NULL));
+}
+
+/*
+ * Solaris Kerberos
+ * Same as krb5_send_tgs plus an extra arg to return the FQDN
+ * of the KDC sent the request.
+ */
+krb5_error_code
+krb5_send_tgs2(krb5_context context, krb5_flags kdcoptions,
+	      const krb5_ticket_times *timestruct, const krb5_enctype *ktypes,
+	      krb5_const_principal sname, krb5_address *const *addrs,
+	      krb5_authdata *const *authorization_data,
+	      krb5_pa_data *const *padata, const krb5_data *second_ticket,
+	    krb5_creds *in_cred, krb5_response *rep, char **hostname_used)
+{
     krb5_error_code retval;
     krb5_kdc_req tgsreq;
     krb5_data *scratch, scratch2;
@@ -271,9 +293,10 @@
     /* now send request & get response from KDC */
 send_again:
     use_master = 0;
-    retval = krb5_sendto_kdc(context, scratch, 
-			     krb5_princ_realm(context, sname),
-			     &rep->response, &use_master, tcp_only);
+    retval = krb5_sendto_kdc2(context, scratch, 
+			    krb5_princ_realm(context, sname),
+			    &rep->response, &use_master, tcp_only,
+			    hostname_used);
     if (retval == 0) {
 	if (krb5_is_krb_error(&rep->response)) {
 	    if (!tcp_only) {
--- a/usr/src/lib/gss_mechs/mech_krb5/krb5/krb/valid_times.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/gss_mechs/mech_krb5/krb5/krb/valid_times.c	Mon Aug 16 17:01:32 2010 -0700
@@ -29,7 +29,6 @@
 
 #include "k5-int.h"
 
-#define in_clock_skew(date) (labs((date)-currenttime) < context->clockskew)
 
 /*
  * This is an internal routine which validates the krb5_timestamps
--- a/usr/src/lib/gss_mechs/mech_krb5/krb5/krb/walk_rtree.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/gss_mechs/mech_krb5/krb5/krb/walk_rtree.c	Mon Aug 16 17:01:32 2010 -0700
@@ -1,4 +1,6 @@
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
+/*
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
+ */
 /*
  * lib/krb5/krb/walk_rtree.c
  *
@@ -86,6 +88,7 @@
 #define CONFIGURABLE_AUTHENTICATION_PATH
 #include "k5-int.h"
 #include "int-proto.h"
+#include <locale.h>
 
 /* internal function, used by krb5_get_cred_from_kdc() */
 
@@ -141,8 +144,25 @@
     printf("  server is %s\n",server->data);
 #endif
 
-    if (!(client->data &&server->data))
-      return KRB5_NO_TKT_IN_RLM;
+    if (!(client->data && server->data)) {
+	/* Solaris Kerberos - enhance error message */
+	if (!client->data && !server->data) {
+	    krb5_set_error_message(context, KRB5_NO_TKT_IN_RLM,
+				dgettext(TEXT_DOMAIN,
+					"Cannot find ticket for requested realm: unknown client and server"));
+	} else {
+	    if (!client->data) {
+		krb5_set_error_message(context, KRB5_NO_TKT_IN_RLM,
+				    dgettext(TEXT_DOMAIN,
+					    "Cannot find ticket for requested realm: unknown client"));
+	    } else {
+	       krb5_set_error_message(context, KRB5_NO_TKT_IN_RLM,
+				    dgettext(TEXT_DOMAIN,
+					    "Cannot find ticket for requested realm: unknown server"));
+	    }
+	}
+	return KRB5_NO_TKT_IN_RLM;
+    }
 #ifdef CONFIGURABLE_AUTHENTICATION_PATH
     if ((cap_client = (char *)malloc(client->length + 1)) == NULL)
 	return ENOMEM;
@@ -198,10 +218,15 @@
 	/* handle case of one ran out */
 	if (!clen) {
 	    /* construct path from client to server, down the tree */
-	    if (!slen)
+	    if (!slen) {
 		/* in the same realm--this means there is no ticket
 		   in this realm. */
+	        krb5_set_error_message(context, KRB5_NO_TKT_IN_RLM,
+				    dgettext(TEXT_DOMAIN,
+					    "Cannot find ticket for requested realm: client is '%s', server is '%s'"),
+				    client->data, server->data);
 		return KRB5_NO_TKT_IN_RLM;
+	    }
 	    if (*scp == realm_branch_char) {
 		/* one is a subdomain of the other */
 		com_cdot = client->data;
--- a/usr/src/lib/gss_mechs/mech_krb5/krb5/os/hostaddr.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/gss_mechs/mech_krb5/krb5/os/hostaddr.c	Mon Aug 16 17:01:32 2010 -0700
@@ -1,3 +1,6 @@
+/*
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
+ */
 /*
  * lib/krb5/os/hostaddr.c
  *
@@ -28,7 +31,7 @@
  */
 
 #include "k5-int.h"
-
+#include <locale.h>
 #include "fake-addrinfo.h"
 
 krb5_error_code
@@ -39,8 +42,9 @@
     int			i, j, r;
     struct addrinfo hints, *ai, *aip;
 
-    if (!name)
+    if (!name) {
 	return KRB5_ERR_BAD_HOSTNAME;
+    }
 
     memset (&hints, 0, sizeof (hints));
     hints.ai_flags = AI_NUMERICHOST;
@@ -55,8 +59,13 @@
 	hints.ai_flags &= ~AI_NUMERICHOST;
 	r = getaddrinfo (name, 0, &hints, &ai);
     }
-    if (r)
+    if (r) {
+        krb5_set_error_message(context, KRB5_ERR_BAD_HOSTNAME,
+			    dgettext(TEXT_DOMAIN,
+				    "Hostname cannot be canonicalized for '%s': %s"),
+			    name, strerror(r));
 	return KRB5_ERR_BAD_HOSTNAME;
+    }
 
     for (i = 0, aip = ai; aip; aip = aip->ai_next) {
 	switch (aip->ai_addr->sa_family) {
--- a/usr/src/lib/gss_mechs/mech_krb5/krb5/os/locate_kdc.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/gss_mechs/mech_krb5/krb5/os/locate_kdc.c	Mon Aug 16 17:01:32 2010 -0700
@@ -31,6 +31,26 @@
  * get socket addresses for KDC.
  */
 
+/*
+ * Solaris Kerberos
+ * Re-factored the following routines to get a clear separation of locating
+ * KDC entries (krb5.conf/DNS-SRVrecs) versus mapping them to net addresses
+ * to allow us to output better error msgs:
+ *   krb5int_locate_server
+ *   prof_locate_server
+ *   dns_locate_server
+ *   krb5_locate_srv_conf_1 (removed)
+ *   krb5_locate_srv_dns_1  (removed)
+ *   prof_hostnames2netaddrs (new)
+ *   hostlist2str (new)
+ *   dns_hostnames2netaddrs (new)
+ *   dnslist2str (new)
+ * Also, for the profile get_master==1 case, the algorithm has been
+ * simplified to just do a profile_get_values on "admin_server" and
+ * not try to match those against "kdc" entries (does not seem necessary
+ * and the DNS-SRVrecs code does not do that).
+ */
+
 #include "fake-addrinfo.h"
 #include "k5-int.h"
 #include "os-proto.h"
@@ -48,6 +68,8 @@
 #ifndef T_SRV
 #define T_SRV 33
 #endif /* T_SRV */
+#include <syslog.h>
+#include <locale.h>
 
 /* for old Unixes and friends ... */
 #ifndef MAXHOSTNAMELEN
@@ -296,261 +318,6 @@
     return err;
 }
 
-/*
- * returns count of number of addresses found
- * if master is non-NULL, it is filled in with the index of
- * the master kdc
- */
-
-static krb5_error_code
-krb5_locate_srv_conf_1(krb5_context context, const krb5_data *realm,
-		       const char * name, struct addrlist *addrlist,
-		       int get_masters, int socktype,
-		       int udpport, int sec_udpport, int family)
-{
-    const char	*realm_srv_names[4];
-    char **masterlist, **hostlist, *host, *port, *cp;
-    krb5_error_code code;
-    int i, j, count, ismaster;
-
-    Tprintf ("looking in krb5.conf for realm %s entry %s; ports %d,%d\n",
-	     realm->data, name, ntohs (udpport), ntohs (sec_udpport));
-
-    if ((host = malloc(realm->length + 1)) == NULL) 
-	return ENOMEM;
-
-    strncpy(host, realm->data, realm->length);
-    host[realm->length] = '\0';
-    hostlist = 0;
-
-    masterlist = NULL;
-
-    realm_srv_names[0] = "realms";
-    realm_srv_names[1] = host;
-    realm_srv_names[2] = name;
-    realm_srv_names[3] = 0;
-
-    code = profile_get_values(context->profile, realm_srv_names, &hostlist);
-
-    if (code) {
-	Tprintf ("config file lookup failed: %s\n",
-		 error_message(code));
-        if (code == PROF_NO_SECTION || code == PROF_NO_RELATION)
-	    code = KRB5_REALM_UNKNOWN;
- 	krb5_xfree(host);
-  	return code;
-     }
-
-    count = 0;
-    while (hostlist && hostlist[count])
-	    count++;
-    Tprintf ("found %d entries under 'kdc'\n", count);
-    
-    if (count == 0) {
-        profile_free_list(hostlist);
-	krb5_xfree(host);
-	addrlist->naddrs = 0;
-	return 0;
-    }
-    
-    if (get_masters) {
-	realm_srv_names[0] = "realms";
-	realm_srv_names[1] = host;
-	realm_srv_names[2] = "admin_server";
-	realm_srv_names[3] = 0;
-
-	code = profile_get_values(context->profile, realm_srv_names,
-				  &masterlist);
-
-	krb5_xfree(host);
-
-	if (code == 0) {
-	    for (i=0; masterlist[i]; i++) {
-		host = masterlist[i];
-
-		/*
-		 * Strip off excess whitespace
-		 */
-		cp = strchr(host, ' ');
-		if (cp)
-		    *cp = 0;
-		cp = strchr(host, '\t');
-		if (cp)
-		    *cp = 0;
-		cp = strchr(host, ':');
-		if (cp)
-		    *cp = 0;
-	    }
-	}
-    } else {
-	krb5_xfree(host);
-    }
-
-    /* at this point, if master is non-NULL, then either the master kdc
-       is required, and there is one, or the master kdc is not required,
-       and there may or may not be one. */
-
-#ifdef HAVE_NETINET_IN_H
-    if (sec_udpport)
-	    count = count * 2;
-#endif
-
-    for (i=0; hostlist[i]; i++) {
-	int p1, p2;
-
-	host = hostlist[i];
-	Tprintf ("entry %d is '%s'\n", i, host);
-	/*
-	 * Strip off excess whitespace
-	 */
-	cp = strchr(host, ' ');
-	if (cp)
-	    *cp = 0;
-	cp = strchr(host, '\t');
-	if (cp)
-	    *cp = 0;
-	port = strchr(host, ':');
-	if (port) {
-	    *port = 0;
-	    port++;
-	}
-
-	ismaster = 0;
-	if (masterlist) {
-	    for (j=0; masterlist[j]; j++) {
-		if (strcasecmp(hostlist[i], masterlist[j]) == 0) {
-		    ismaster = 1;
-		}
-	    }
-	}
-
-	if (get_masters && !ismaster)
-	    continue;
-
-	if (port) {
-	    unsigned long l;
-#ifdef HAVE_STROUL
-	    char *endptr;
-	    l = strtoul (port, &endptr, 10);
-	    if (endptr == NULL || *endptr != 0)
-		return EINVAL;
-#else
-	    l = atoi (port);
-#endif
-	    /* L is unsigned, don't need to check <0.  */
-	    if (l > 65535)
-		return EINVAL;
-	    p1 = htons (l);
-	    p2 = 0;
-	} else {
-	    p1 = udpport;
-	    p2 = sec_udpport;
-	}
-
-	if (socktype != 0)
-	    code = add_host_to_list (addrlist, hostlist[i], p1, p2,
-				     socktype, family);
-	else {
-	    code = add_host_to_list (addrlist, hostlist[i], p1, p2,
-				     SOCK_DGRAM, family);
-	    if (code == 0)
-		code = add_host_to_list (addrlist, hostlist[i], p1, p2,
-					 SOCK_STREAM, family);
-	}
-	if (code) {
-	    Tprintf ("error %d (%s) returned from add_host_to_list\n", code,
-		     error_message (code));
-	    if (hostlist)
-		profile_free_list (hostlist);
-	    if (masterlist)
-		profile_free_list (masterlist);
-	    return code;
-	}
-    }
-
-    if (hostlist)
-        profile_free_list(hostlist);
-    if (masterlist)
-        profile_free_list(masterlist);
-
-    return 0;
-}
-
-#ifdef TEST
-static krb5_error_code
-krb5_locate_srv_conf(krb5_context context, const krb5_data *realm,
-		     const char *name, struct addrlist *al, int get_masters,
-		     int udpport, int sec_udpport)
-{
-    krb5_error_code ret;
-
-    ret = krb5_locate_srv_conf_1 (context, realm, name, al,
-				  get_masters, 0, udpport, sec_udpport, 0);
-    if (ret)
-	return ret;
-    if (al->naddrs == 0)	/* Couldn't resolve any KDC names */
-	return KRB5_REALM_CANT_RESOLVE;
-    return 0;
-}
-#endif
-
-#ifdef KRB5_DNS_LOOKUP
-static krb5_error_code
-krb5_locate_srv_dns_1 (const krb5_data *realm,
-		       const char *service,
-		       const char *protocol,
-		       struct addrlist *addrlist,
-		       int family)
-{
-    struct srv_dns_entry *head = NULL;
-    struct srv_dns_entry *entry = NULL, *next;
-    krb5_error_code code = 0;
-
-    code = krb5int_make_srv_query_realm(realm, service, protocol, &head);
-    if (code)
-	return 0;
-
-    /*
-     * Okay!  Now we've got a linked list of entries sorted by
-     * priority.  Start looking up A records and returning
-     * addresses.
-     */
-
-    if (head == NULL)
-	return 0;
-
-    /* Check for the "." case indicating no support.  */
-    if (head->next == 0 && head->host[0] == 0) {
-	free(head->host);
-	free(head);
-	return KRB5_ERR_NO_SERVICE;
-    }
-
-    Tprintf ("walking answer list:\n");
-    for (entry = head; entry != NULL; entry = next) {
-	Tprintf ("\tport=%d host=%s\n", entry->port, entry->host);
-	next = entry->next;
-	code = add_host_to_list (addrlist, entry->host, htons (entry->port), 0,
-				 (strcmp("_tcp", protocol)
-				  ? SOCK_DGRAM
-				  : SOCK_STREAM), family);
-	if (code) {
-	    break;
-	}
-	if (entry == head) {
-	    free(entry->host);
-	    free(entry);
-	    head = next;
-	    entry = 0;
-	}
-    }
-    Tprintf ("[end]\n");
-
-    krb5int_free_srv_dns_data(head);
-    return code;
-}
-#endif
-
 #include <locate_plugin.h>
 
 #if TARGET_OS_MAC
@@ -681,56 +448,75 @@
 
 static krb5_error_code
 prof_locate_server (krb5_context context, const krb5_data *realm,
-		    struct addrlist *addrlist,
-		    enum locate_service_type svc, int socktype, int family)
+		    char ***hostlist,
+		    enum locate_service_type svc)
 {
-    const char *profname;
-    int dflport1, dflport2 = 0;
-    struct servent *serv;
+    const char	*realm_srv_names[4];
+    char **hl, *host, *profname;
+    krb5_error_code code;
+    int i, j, count;
+
+    *hostlist = NULL;  /* default - indicate no KDCs found */
 
     switch (svc) {
     case locate_service_kdc:
 	profname = "kdc";
-	/* We used to use /etc/services for these, but enough systems
-	   have old, crufty, wrong settings that this is probably
-	   better.  */
-    kdc_ports:
-	dflport1 = htons(KRB5_DEFAULT_PORT);
-	dflport2 = htons(KRB5_DEFAULT_SEC_PORT);
 	break;
     case locate_service_master_kdc:
-	profname = "master_kdc";
-	goto kdc_ports;
+        profname = "master_kdc";
+	break;
     case locate_service_kadmin:
 	profname = "admin_server";
-	dflport1 = htons(DEFAULT_KADM5_PORT);
 	break;
     case locate_service_krb524:
 	profname = "krb524_server";
-	serv = getservbyname(KRB524_SERVICE, "udp");
-	dflport1 = serv ? serv->s_port : htons (KRB524_PORT);
 	break;
     case locate_service_kpasswd:
 	profname = "kpasswd_server";
-	dflport1 = htons(DEFAULT_KPASSWD_PORT);
 	break;
     default:
-	return EBUSY;		/* XXX */
+	return EINVAL;
     }
 
-    return krb5_locate_srv_conf_1 (context, realm, profname, addrlist,
-				   0, socktype,
-				   dflport1, dflport2, family);
+    if ((host = malloc(realm->length + 1)) == NULL) 
+	return ENOMEM;
+
+    (void) strncpy(host, realm->data, realm->length);
+    host[realm->length] = '\0';
+    hl = 0;
+
+    realm_srv_names[0] = "realms";
+    realm_srv_names[1] = host;
+    realm_srv_names[2] = profname;
+    realm_srv_names[3] = 0;
+
+    code = profile_get_values(context->profile, realm_srv_names, &hl);
+    if (code) {
+	Tprintf ("config file lookup failed: %s\n",
+		 error_message(code));
+        if (code == PROF_NO_SECTION || code == PROF_NO_RELATION)
+	    code = KRB5_REALM_UNKNOWN;
+ 	krb5_xfree(host);
+  	return code;
+     }
+    krb5_xfree(host);
+
+    *hostlist = hl;
+
+    return 0;
 }
 
 static krb5_error_code
 dns_locate_server (krb5_context context, const krb5_data *realm,
-		   struct addrlist *addrlist,
-		   enum locate_service_type svc, int socktype, int family)
+		struct srv_dns_entry **dns_list_head,
+		enum locate_service_type svc, int socktype, int family)
 {
     const char *dnsname;
     int use_dns = _krb5_use_dns_kdc(context);
     krb5_error_code code;
+    struct srv_dns_entry *head = NULL;
+
+    *dns_list_head = NULL; /* default: indicate we have found no KDCs */
 
     if (!use_dns)
 	return KRB5_PLUGIN_NO_HANDLE;
@@ -757,15 +543,242 @@
 
     code = 0;
     if (socktype == SOCK_DGRAM || socktype == 0) {
-	code = krb5_locate_srv_dns_1(realm, dnsname, "_udp", addrlist, family);
+	code = krb5int_make_srv_query_realm(realm, dnsname, "_udp", &head);
 	if (code)
 	    Tprintf("dns udp lookup returned error %d\n", code);
     }
     if ((socktype == SOCK_STREAM || socktype == 0) && code == 0) {
-	code = krb5_locate_srv_dns_1(realm, dnsname, "_tcp", addrlist, family);
+	code = krb5int_make_srv_query_realm(realm, dnsname, "_tcp", &head);
 	if (code)
 	    Tprintf("dns tcp lookup returned error %d\n", code);
     }
+
+    if (head == NULL)
+	return 0;
+
+    /* Check for the "." case indicating no support.  */
+    if (head->next == 0 && head->host[0] == 0) {
+	free(head->host);
+	free(head);
+	return KRB5_ERR_NO_SERVICE;
+    }
+
+    /*
+     * Okay!  Now we've got a linked list of entries sorted by
+     * priority.  Return it so later we can map hostnames to net addresses.
+     */
+    *dns_list_head = head;
+
+    return 0;
+}
+
+/*
+ * Given the list of hostnames of KDCs found in DNS SRV recs, lets go
+ * thru NSS (name svc switch) to get the net addrs.
+ */
+static krb5_error_code
+dns_hostnames2netaddrs(
+	struct srv_dns_entry *head,
+	enum locate_service_type svc,
+	int socktype,
+	int family,
+	struct addrlist *addrlist)
+{
+    struct srv_dns_entry *entry = NULL, *next;
+    krb5_error_code code;
+
+    Tprintf ("walking answer list:\n");
+    for (entry = head; entry != NULL; entry = entry->next) {
+	code = 0;
+	if (socktype)
+	    code = add_host_to_list (addrlist, entry->host,
+				    htons (entry->port), 0,
+				    socktype, family);
+	else {
+	    (void) add_host_to_list (addrlist, entry->host,
+				    htons (entry->port), 0,
+				    SOCK_DGRAM, family);
+		
+	    code = add_host_to_list (addrlist, entry->host,
+				    htons (entry->port), 0,
+				    SOCK_STREAM, family);
+	}
+        if (code) {
+	    Tprintf("  fail add_host code=%d %s\n", code, entry->host);
+        }
+    }
+    Tprintf ("[end]\n");
+
+    return code;
+}
+
+/*
+ * Given the DNS SRV recs list, return a string of all the hosts like so:
+ *     "fqdn0[,fqdn1][,fqdnN]"
+ */
+static char *
+dnslist2str(struct srv_dns_entry *dns_list_head)
+{
+	struct srv_dns_entry *head = dns_list_head;
+	struct srv_dns_entry *entry = NULL, *next;
+	unsigned int size = 0, c = 0, buf_size;
+	char *s = NULL;
+
+	for (entry = head; entry; entry = entry->next, c++) {
+		size += strlen(entry->host);
+	}
+	if (!c)
+		return NULL;
+
+	/* hostnames + commas + NULL */
+	buf_size = size + (c - 1) + 1;
+	s = malloc(buf_size);
+	if (!s)
+		return NULL;
+
+	(void) strlcpy(s, head->host, buf_size);
+	for (entry = head->next; entry; entry = entry->next) {
+	    (void) strlcat(s, ",", buf_size);
+	    (void) strlcat(s, entry->host, buf_size);
+	}
+
+	return s;
+}
+
+/*
+ * Given the profile hostlist, return a string of all the hosts like so:
+ *     "fqdn0[,fqdn1][,fqdnN]"
+ */
+static char *
+hostlist2str(char **hostlist)
+{
+	unsigned int c = 0, size = 0, buf_size;
+	char **hl = hostlist, *s = NULL;
+	
+	while (hl && *hl) {
+	    size += strlen(*hl);
+	    hl++;
+	    c++;
+	}
+	if (!c)
+	    return NULL;
+
+	/* hostnames + commas + NULL */
+	buf_size = size + (c - 1) + 1;
+	s = malloc(buf_size);
+	if (!s)
+	    return NULL;
+
+	hl = hostlist;
+	(void) strlcpy(s, *hl, buf_size);
+	hl++;
+	while (hl && *hl) {
+	    (void) strlcat(s, ",", buf_size);
+	    (void) strlcat(s, *hl, buf_size);
+	    hl++;
+	}
+
+	return s;
+}
+
+/*
+ * Take the profile KDC list and return a list of net addrs.
+ */
+static krb5_error_code
+prof_hostnames2netaddrs(
+	char **hostlist,
+	enum locate_service_type svc,
+	int socktype,
+	int family,
+	struct addrlist *addrlist) /* output */
+{
+	int udpport  = 0 , sec_udpport = 0;
+	int code, i;
+	struct servent *serv;
+
+	int count = 0;
+	while (hostlist && hostlist[count])
+		count++;
+	if (count == 0) {
+		return 0;
+	}
+    
+    switch (svc) {
+    case locate_service_kdc:
+    case locate_service_master_kdc:
+	/* We used to use /etc/services for these, but enough systems
+	   have old, crufty, wrong settings that this is probably
+	   better.  */
+	udpport = htons(KRB5_DEFAULT_PORT);
+	sec_udpport = htons(KRB5_DEFAULT_SEC_PORT);
+	break;
+    case locate_service_kadmin:
+	udpport = htons(DEFAULT_KADM5_PORT);
+	break;
+    case locate_service_krb524:
+	serv = getservbyname(KRB524_SERVICE, "udp");
+	udpport = serv ? serv->s_port : htons (KRB524_PORT);
+	break;
+    case locate_service_kpasswd:
+	udpport = htons(DEFAULT_KPASSWD_PORT);
+	break;
+    default:
+	return EINVAL;
+    }
+
+    for (i=0; hostlist[i]; i++) {
+	int p1, p2;
+	char *cp, *port, *host;
+
+	host = hostlist[i];
+	/*
+	 * Strip off excess whitespace
+	 */
+	cp = strchr(host, ' ');
+	if (cp)
+	    *cp = 0;
+	cp = strchr(host, '\t');
+	if (cp)
+	    *cp = 0;
+	port = strchr(host, ':');
+	if (port) {
+	    *port = 0;
+	    port++;
+	}
+
+	if (port) {
+	    unsigned long l;
+#ifdef HAVE_STROUL
+	    char *endptr;
+	    l = strtoul (port, &endptr, 10);
+	    if (endptr == NULL || *endptr != 0)
+		return EINVAL;
+#else
+	    l = atoi (port);
+#endif
+	    /* L is unsigned, don't need to check <0.  */
+	    if (l == 0 || l > 65535)
+		return EINVAL;
+	    p1 = htons (l);
+	    p2 = 0;
+	} else {
+	    p1 = udpport;
+	    p2 = sec_udpport;
+	}
+
+
+	if (socktype != 0) {
+	    code = add_host_to_list (addrlist, hostlist[i], p1, p2,
+				     socktype, family);
+	} else {
+	    code = add_host_to_list (addrlist, hostlist[i], p1, p2,
+				     SOCK_DGRAM, family);
+	    if (code == 0)
+		code = add_host_to_list (addrlist, hostlist[i], p1, p2,
+					 SOCK_STREAM, family);
+	}
+    }
+
     return code;
 }
 
@@ -781,6 +794,8 @@
 {
     krb5_error_code code;
     struct addrlist al = ADDRLIST_INIT;
+    char **hostlist = NULL;
+    struct srv_dns_entry *dns_list_head = NULL;
 
     *addrlist = al;
 
@@ -792,19 +807,18 @@
 	 * is no way to indicate "service not available" via the
 	 * config file.
 	 */
-
-	code = prof_locate_server(context, realm, &al, svc, socktype, family);
+	code = prof_locate_server(context, realm, &hostlist, svc);
 
 	/*
 	 * Solaris Kerberos:
 	 * If kpasswd_server has not been configured and dns_lookup_kdc -
 	 * dns_fallback are not configured then admin_server should
-	 * be inferenced, per krb5.conf(4).
+	 * be inferred, per krb5.conf(4).
 	 */
 	if (code && svc == locate_service_kpasswd &&
 	    !maybe_use_dns(context, "dns_lookup_kdc", 0)) {
-		code = krb5_locate_srv_conf_1(context, realm, "admin_server",
-		    &al, 0, socktype, htons(DEFAULT_KPASSWD_PORT), 0, family);
+		code = prof_locate_server(context, realm, &hostlist,
+			locate_service_kadmin);
 	}
 
 #ifdef KRB5_DNS_LOOKUP
@@ -816,41 +830,8 @@
 	/* Try DNS for all profile errors?  */
 	if (code && !krb5_is_referral_realm(realm)) {
 	    krb5_error_code code2;
-	    code2 = dns_locate_server(context, realm, &al, svc, socktype,
-				      family);
-
-	    /*
-	     * Solaris Kerberos:
-	     * If an entry for _kerberos-master. does not exist (checked for
-	     * above) but _kpasswd. does then treat that as an entry for the
-	     * master KDC (but use port 88 not the kpasswd port). MS AD creates
-	     * kpasswd entries by default in DNS.
-	     */
-	    if (code2 == 0 && svc == locate_service_master_kdc &&
-		al.naddrs == 0) {
-
-		/* Look for _kpasswd._tcp|udp */
-		code2 = dns_locate_server(context, realm, &al,
-		    locate_service_kpasswd, socktype, family);
-
-		/* Set the port to 88 instead of the kpasswd port */
-		if (code2 == 0 ) {
-		    int i;
-		    struct addrinfo *a;
-
-		    for (i = 0; i < al.naddrs; i++) {
-			if (al.addrs[i].ai->ai_family == AF_INET)
-			    for (a = al.addrs[i].ai; a != NULL; a = a->ai_next)
-				((struct sockaddr_in *)a->ai_addr)->sin_port =
-				    htons(KRB5_DEFAULT_PORT);
-
-			if (al.addrs[i].ai->ai_family == AF_INET6)
-			    for (a = al.addrs[i].ai; a != NULL; a = a->ai_next)
-				((struct sockaddr_in6 *)a->ai_addr)->sin6_port =
-				    htons(KRB5_DEFAULT_PORT);
-		    }
-		}
-	    }
+	    code2 = dns_locate_server(context, realm, &dns_list_head,
+				    svc, socktype, family);
 
 	    if (code2 != KRB5_PLUGIN_NO_HANDLE)
 		code = code2;
@@ -860,26 +841,148 @@
 	/* We could put more heuristics here, like looking up a hostname
 	   of "kerberos."+REALM, etc.  */
     }
-    if (code == 0)
-	Tprintf ("krb5int_locate_server found %d addresses\n",
-		 al.naddrs);
-    else
-	Tprintf ("krb5int_locate_server returning error code %d/%s\n",
-		 code, error_message(code));
+
     if (code != 0) {
 	if (al.space)
 	    free_list (&al);
+	if (hostlist)
+	    profile_free_list(hostlist);
+	if (dns_list_head)
+	    krb5int_free_srv_dns_data(dns_list_head);
+
 	return code;
     }
-    if (al.naddrs == 0) {	/* No good servers */
+
+    /*
+     * At this point we have no errors, let's check to see if we have
+     * any KDC entries from krb5.conf or DNS.
+     */
+    if (!hostlist && !dns_list_head) {
+	switch(svc) {
+	case locate_service_master_kdc:
+	    krb5_set_error_message(context,
+				KRB5_REALM_CANT_RESOLVE,
+				dgettext(TEXT_DOMAIN,
+					"Cannot find a master KDC entry in krb5.conf(4) or DNS Service Location records for realm '%.*s'"),
+				realm->length, realm->data);
+	    break;
+	case locate_service_kadmin:
+	    krb5_set_error_message(context,
+				KRB5_REALM_CANT_RESOLVE,
+				dgettext(TEXT_DOMAIN,
+					"Cannot find a kadmin KDC entry in krb5.conf(4) or DNS Service Location records for realm '%.*s'"),
+				realm->length, realm->data);
+	    break;
+	case locate_service_kpasswd:
+	    krb5_set_error_message(context,
+				KRB5_REALM_CANT_RESOLVE,
+				dgettext(TEXT_DOMAIN,
+					"Cannot find a kpasswd KDC entry in krb5.conf(4) or DNS Service Location records for realm '%.*s'"),
+				realm->length, realm->data);
+	    break;
+	default: 	  /*  locate_service_kdc: */
+		krb5_set_error_message(context,
+				    KRB5_REALM_CANT_RESOLVE,
+				    dgettext(TEXT_DOMAIN,
+					    "Cannot find any KDC entries in krb5.conf(4) or DNS Service Location records for realm '%.*s'"),
+				    realm->length, realm->data);
+			       
+	}
+	return KRB5_REALM_CANT_RESOLVE;
+    }
+
+    /* We have KDC entries, let see if we can get their net addrs. */
+    if (hostlist)
+	code = prof_hostnames2netaddrs(hostlist, svc,
+				    socktype, family, &al);
+    else if (dns_list_head)
+	code = dns_hostnames2netaddrs(dns_list_head, svc,
+				    socktype, family, &al);
+    if (code) {
+	if (hostlist)
+	    profile_free_list(hostlist);
+	if (dns_list_head)
+	    krb5int_free_srv_dns_data(dns_list_head);
+	return code;
+    }
+
+    /*
+     * Solaris Kerberos:
+     * If an entry for _kerberos-master. does not exist (checked for
+     * above) but _kpasswd. does then treat that as an entry for the
+     * master KDC (but use port 88 not the kpasswd port). MS AD creates
+     * kpasswd entries by default in DNS.
+     */
+    if (!dns_list_head && svc == locate_service_master_kdc &&
+	al.naddrs == 0) {
+
+	/* Look for _kpasswd._tcp|udp */
+	code = dns_locate_server(context, realm, &dns_list_head,
+				locate_service_kpasswd, socktype, family);
+
+	if (code == 0 && dns_list_head) {
+	    int i;
+	    struct addrinfo *a;
+
+	    code = dns_hostnames2netaddrs(dns_list_head, svc,
+					socktype, family, &al);
+
+	    /* Set the port to 88 instead of the kpasswd port */
+	    if (code == 0 && al.naddrs > 0) {
+		for (i = 0; i < al.naddrs; i++) {
+		    if (al.addrs[i].ai->ai_family == AF_INET)
+			for (a = al.addrs[i].ai; a != NULL; a = a->ai_next)
+			    ((struct sockaddr_in *)a->ai_addr)->sin_port =
+				htons(KRB5_DEFAULT_PORT);
+				
+		    if (al.addrs[i].ai->ai_family == AF_INET6)
+			for (a = al.addrs[i].ai; a != NULL; a = a->ai_next)
+			     ((struct sockaddr_in6 *)a->ai_addr)->sin6_port =
+				    htons(KRB5_DEFAULT_PORT);
+		}
+	    }
+	}
+    }
+
+    /* No errors so far, lets see if we have KDC net addrs */
+    if (al.naddrs == 0) {
+	char *hostlist_str = NULL, *dnslist_str  = NULL;
 	if (al.space)
 	    free_list (&al);
-	krb5_set_error_message(context, KRB5_REALM_CANT_RESOLVE,
-			       "Cannot resolve network address for KDC in realm %.*s",
-			       realm->length, realm->data);
-			       
+
+	if (hostlist) {
+	    hostlist_str = hostlist2str(hostlist);
+	    krb5_set_error_message(context, KRB5_REALM_CANT_RESOLVE,
+				dgettext(TEXT_DOMAIN,
+					"Cannot resolve network address for KDCs '%s' specified in krb5.conf(4) for realm %.*s"),
+				hostlist_str ? hostlist_str : "unknown",
+				realm->length, realm->data);
+	    if (hostlist_str)
+		free(hostlist_str);
+	} else if (dns_list_head) {
+	    dnslist_str = dnslist2str(dns_list_head);
+	    krb5_set_error_message(context, KRB5_REALM_CANT_RESOLVE,
+				dgettext(TEXT_DOMAIN,
+					"Cannot resolve network address for KDCs '%s' discovered via DNS Service Location records for realm '%.*s'"),
+				dnslist_str ? dnslist_str : "unknown",
+				realm->length, realm->data);
+	    if (dnslist_str)
+		    free(dnslist_str);
+	}
+
+	if (hostlist)
+	    profile_free_list(hostlist);
+	if (dns_list_head)
+	    krb5int_free_srv_dns_data(dns_list_head);
+
 	return KRB5_REALM_CANT_RESOLVE;
     }
+
+    if (hostlist)
+	    profile_free_list(hostlist);
+    if (dns_list_head)
+	    krb5int_free_srv_dns_data(dns_list_head);
+
     *addrlist = al;
     return 0;
 }
--- a/usr/src/lib/gss_mechs/mech_krb5/krb5/os/sendto_kdc.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/gss_mechs/mech_krb5/krb5/os/sendto_kdc.c	Mon Aug 16 17:01:32 2010 -0700
@@ -36,6 +36,7 @@
 
 /* Solaris Kerberos */
 #include <syslog.h>
+#include <locale.h>
 
 #ifdef HAVE_SYS_TIME_H
 #include <sys/time.h>
@@ -377,6 +378,21 @@
 		 const krb5_data *realm, krb5_data *reply,
 		 int *use_master, int tcp_only)
 {
+	return (krb5_sendto_kdc2(context, message, realm, reply, use_master,
+				tcp_only, NULL));
+}
+
+/*
+ * Solaris Kerberos
+ * Same as krb5_sendto_kdc plus an extra arg to return the FQDN
+ * of the KDC sent the request.
+ * Caller (at top of stack) needs to free hostname_used.
+ */
+krb5_error_code
+krb5_sendto_kdc2 (krb5_context context, const krb5_data *message,
+		 const krb5_data *realm, krb5_data *reply,
+		int *use_master, int tcp_only, char **hostname_used)
+{
     krb5_error_code retval, retval2;
     struct addrlist addrs = ADDRLIST_INIT;	/* Solaris Kerberos */
     int socktype1 = 0, socktype2 = 0, addr_used;
@@ -470,6 +486,24 @@
                     krb5int_free_addrlist (&addrs3);
                 }
             }
+
+	    if (hostname_used) {
+		struct sockaddr *sa;
+		char buf[NI_MAXHOST];
+		int err;
+
+		*hostname_used = NULL;
+		sa = addrs.addrs[addr_used].ai->ai_addr;
+		err = getnameinfo (sa, socklen (sa), buf, sizeof (buf), 0, 0,
+				AI_CANONNAME);
+		if (err)
+		    err = getnameinfo (sa, socklen (sa), buf,
+				    sizeof (buf), 0, 0,
+				    NI_NUMERICHOST);
+		if (!err)
+		    *hostname_used = strdup(buf);
+	            /* don't sweat strdup fail */
+	    }
             krb5int_free_addrlist (&addrs);
             return 0;
 	default:
@@ -480,8 +514,9 @@
 		retval = KRB5KDC_ERR_SVC_UNAVAILABLE;
 	    } else {
 		krb5_set_error_message(context, retval,
-				       "Cannot contact any KDC for realm '%.*s'",
-				       realm->length, realm->data);
+				    dgettext(TEXT_DOMAIN,
+				    "Cannot contact any KDC for realm '%.*s'"),
+				    realm->length, realm->data);
 	    }
 	    break;
 	}
--- a/usr/src/lib/gss_mechs/mech_krb5/krb5/os/sn2princ.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/gss_mechs/mech_krb5/krb5/os/sn2princ.c	Mon Aug 16 17:01:32 2010 -0700
@@ -1,8 +1,6 @@
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
-
 /*
  * lib/krb5/os/sn2princ.c
  *
@@ -40,6 +38,8 @@
 #ifdef HAVE_SYS_PARAM_H
 #include <sys/param.h>
 #endif
+#include <locale.h>
+#include <syslog.h>
 
 #if !defined(DEFAULT_RDNS_LOOKUP)
 /* Solaris Kerberos */
@@ -94,7 +94,6 @@
     printf("krb5_sname_to_principal(host=%s, sname=%s, type=%d)\n",hostname,sname,type);
     printf("      name types: 0=unknown, 3=srv_host\n");
 #endif
-
     if ((type == KRB5_NT_UNKNOWN) ||
 	(type == KRB5_NT_SRV_HST)) {
 
@@ -157,6 +156,11 @@
 		KRB5_LOG(KRB5_ERR, "krb5_sname_to_principal()"
 		       " can't get AF_INET or AF_INET6 addr,"
 		       " err = %d", err);
+
+		krb5_set_error_message(context, KRB5_ERR_BAD_HOSTNAME,
+				    dgettext(TEXT_DOMAIN,
+					    "Hostname cannot be canonicalized for '%s': %s"),
+				    hostname, strerror(err));
 		return KRB5_ERR_BAD_HOSTNAME;
 	    }
 	    remote_host = strdup(hp ? hp->h_name : hostname);
@@ -239,6 +243,12 @@
 #endif
 
 	if (!hrealms[0]) {
+	    /* Solaris Kerberos */
+	    krb5_set_error_message(context, KRB5_ERR_HOST_REALM_UNKNOWN,
+				dgettext(TEXT_DOMAIN,
+					"Cannot determine realm for host: host is '%s'"),
+				remote_host ? remote_host : "unknown");
+
 	    free(remote_host);
 	    krb5_xfree(hrealms);
 	    return KRB5_ERR_HOST_REALM_UNKNOWN;
--- a/usr/src/lib/gss_mechs/mech_krb5/krb5/rcache/rc_io.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/gss_mechs/mech_krb5/krb5/rcache/rc_io.c	Mon Aug 16 17:01:32 2010 -0700
@@ -1,6 +1,5 @@
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 
@@ -30,6 +29,7 @@
 #include <sys/types.h>
 #include <unistd.h>
 #include <syslog.h> /* SUNW */
+#include <locale.h> /* Solaris Kerberos */
 #include "rc_base.h"
 #include "rc_file.h"
 #include "rc_io.h"
@@ -153,16 +153,20 @@
 	case EEXIST:
 	    retval = KRB5_RC_IO_PERM;
 	    krb5_set_error_message(context, retval,
-				   "Cannot create replay cache: %s",
-				   strerror(errno));
+				dgettext(TEXT_DOMAIN,
+					"Cannot create replay cache %s: %s"),
+				d->fn ? d->fn : "<null>",
+				strerror(errno));
 	    do_not_unlink = 1;
 	    goto cleanup;
 
 	default:
 	    retval = KRB5_RC_IO_UNKNOWN;
 	    krb5_set_error_message(context, retval,
-				   "Cannot create replay cache: %s",
-				   strerror(errno));
+				dgettext(TEXT_DOMAIN,
+					"Cannot create replay cache %s: %s"),
+				d->fn ? d->fn : "<null>",
+				strerror(errno));
 	    goto cleanup;
 	}
     }
@@ -296,14 +300,16 @@
 	    case EROFS:
 		retval = KRB5_RC_IO_PERM;
 	    	krb5_set_error_message (context, retval,
-			    "Cannot open replay cache %s: %s",
+			    dgettext(TEXT_DOMAIN,
+				"Cannot open replay cache %s: %s"),
 			    d->fn, strerror(errno));
 		break;
 
 	    default:
 		retval = KRB5_RC_IO_UNKNOWN;
 		krb5_set_error_message (context, retval,
-			    "Cannot open replay cache %s: %s",
+			    dgettext(TEXT_DOMAIN,
+				"Cannot open replay cache %s: %s"),
 			    d->fn, strerror(errno));
 	}
     }
@@ -416,19 +422,22 @@
 	case EFBIG:
 	case ENOSPC:
 	    krb5_set_error_message (context, KRB5_RC_IO_SPACE,
-				    "Can't write to replay cache: %s",
-				    strerror(errno));
+				    dgettext(TEXT_DOMAIN,
+					    "Can't write to replay cache %s: %s"),
+				    d->fn, strerror(errno));
 	    return KRB5_RC_IO_SPACE;
 	case EIO:
 	    krb5_set_error_message (context, KRB5_RC_IO_IO,
-				    "Can't write to replay cache: %s",
-				    strerror(errno));
+				    dgettext(TEXT_DOMAIN,
+					    "Can't write to replay cache %s: %s"),
+				    d->fn, strerror(errno));
 	    return KRB5_RC_IO_IO;
 	case EBADF:
 	default:
 	    krb5_set_error_message (context, KRB5_RC_IO_UNKNOWN,
-				    "Can't write to replay cache: %s",
-				    strerror(errno));
+				    dgettext(TEXT_DOMAIN,
+					    "Can't write to replay cache %s: %s"),
+				    d->fn, strerror(errno));
 	    return KRB5_RC_IO_UNKNOWN;
 	}
     return 0;
@@ -449,8 +458,9 @@
 	case EIO: return KRB5_RC_IO_IO;
 	default:
 	    krb5_set_error_message(context, KRB5_RC_IO_UNKNOWN,
-				   "Cannot sync replay cache file: %s",
-				   strerror(errno));
+				dgettext(TEXT_DOMAIN,
+					"Cannot sync replay cache file %s: %s"),
+				d->fn, strerror(errno));
 	    return KRB5_RC_IO_UNKNOWN;
 	}
     }
@@ -470,8 +480,9 @@
 	case EBADF:
 	default:
 	    krb5_set_error_message(context, KRB5_RC_IO_UNKNOWN,
-				   "Can't read from replay cache: %s",
-				   strerror(errno));
+				dgettext(TEXT_DOMAIN,
+					"Can't read from replay cache %s: %s"),
+				d->fn, strerror(errno));
 	    return KRB5_RC_IO_UNKNOWN;
 	}
     if (count == 0)
@@ -504,21 +515,24 @@
 	{
 	case EIO:
 	    krb5_set_error_message(context, KRB5_RC_IO_IO,
-				   "Can't destroy replay cache: %s",
-				   strerror(errno));
+				dgettext(TEXT_DOMAIN,
+					"Can't destroy replay cache %s: %s"),
+				d->fn, strerror(errno));
 	    return KRB5_RC_IO_IO;
 	case EPERM:
 	case EBUSY:
 	case EROFS:
 	    krb5_set_error_message(context, KRB5_RC_IO_PERM,
-				   "Can't destroy replay cache: %s",
-				   strerror(errno));
+				dgettext(TEXT_DOMAIN,
+					"Can't destroy replay cache %s: %s"),
+				d->fn, strerror(errno));
 	    return KRB5_RC_IO_PERM;
 	case EBADF:
 	default:
 	    krb5_set_error_message(context, KRB5_RC_IO_UNKNOWN,
-				   "Can't destroy replay cache: %s",
-				   strerror(errno));
+				dgettext(TEXT_DOMAIN,
+					"Can't destroy replay cache %s: %s"),
+				d->fn, strerror(errno));
 	    return KRB5_RC_IO_UNKNOWN;
 	}
     return 0;
--- a/usr/src/lib/gss_mechs/mech_krb5/mapfile-vers	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/gss_mechs/mech_krb5/mapfile-vers	Mon Aug 16 17:01:32 2010 -0700
@@ -211,8 +211,14 @@
 	encode_krb5_tgs_req;
 	encode_krb5_ticket;
 	error_message;
+	generic_gss_oid_to_str;
+	generic_gss_release_buffer;
 	ggss_error_table;
 	gss_krb5int_get_tkt_flags;
+	gssint_g_display_major_status;
+	gssint_g_make_string_buffer;
+	gssint_mecherrmap_map;
+	gssint_mecherrmap_map_errcode;
 	gsskrb5_extract_authz_data_from_sec_context;
 	gss_krb5_ccache_name;
 	gss_krb5_copy_ccache;
@@ -485,6 +491,7 @@
 	krb5_get_time_offsets;
 	krb5_get_validated_creds;
 	krb5_getenv;
+	krb5_gss_display_status2;
 	krb5_gss_import_name;
 	krb5_gss_oid_array;
 	krb5_gss_userok;
@@ -724,6 +731,7 @@
 	krb5int_get_error;
 	krb5int_getnameinfo;
 	krb5int_get_plugin_dir_data;
+	krb5int_getspecific;
 	krb5int_gmt_mktime;
 	krb5int_hash_sha1;
 	krb5int_init_context_kdc;
@@ -733,7 +741,9 @@
 	krb5int_open_plugin_dirs;
 	krb5int_pbkdf2_hmac_sha1;
 	krb5int_pthread_loaded;
+	krb5int_key_register;
 	krb5int_sendtokdc_debug_handler;
+	krb5int_setspecific;
 	krb5int_vset_error;
 	kwarn_add_warning;
 	kwarn_del_warning;
--- a/usr/src/lib/gss_mechs/mech_krb5/mech/accept_sec_context.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/gss_mechs/mech_krb5/mech/accept_sec_context.c	Mon Aug 16 17:01:32 2010 -0700
@@ -1,3 +1,6 @@
+/*
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
+ */
 /*
  * Copyright 2000, 2004  by the Massachusetts Institute of Technology.
  * All Rights Reserved.
@@ -70,10 +73,6 @@
  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  */
 
-/*
- * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
- */
-
 #include "k5-int.h"
 #include "gssapiP_krb5.h"
 #ifdef HAVE_MEMORY_H
@@ -81,12 +80,13 @@
 #endif
 #include <assert.h>
 #include "auth_con.h"
-
 #ifdef CFX_EXERCISE
 #define CFX_ACCEPTOR_SUBKEY (time(0) & 1)
 #else
 #define CFX_ACCEPTOR_SUBKEY 1
 #endif
+#include <syslog.h>
+#include <locale.h> /* Solaris Kerberos */
 
 /*
  * Decode, decrypt and store the forwarded creds in the local ccache.
@@ -297,7 +297,8 @@
    krb5_address addr, *paddr;
    krb5_authenticator *authdat = 0;
    krb5_checksum reqcksum;
-   krb5_principal name = NULL;
+   krb5_principal client_name = NULL;
+   krb5_principal server_name = NULL;
    krb5_ui_4 gss_flags = 0;
    krb5_timestamp now;
    gss_buffer_desc token;
@@ -316,6 +317,7 @@
    int cred_rcache = 0;
    int no_encap;
    OM_uint32 t_minor_status = 0;
+   int acquire_fail = 0;
 
    KRB5_LOG0(KRB5_INFO,"krb5_gss_accept_sec_context() start");
 
@@ -477,7 +479,6 @@
 					    NULL, NULL);
 
        if (major_status != GSS_S_COMPLETE){
-
 	   /* Solaris kerberos: RFC2743 indicate this should be returned if we
 	    * can't aquire a default cred.
 	    */
@@ -485,7 +486,7 @@
 		  "krb5_gss_acquire_cred() error"
 		   "orig major_status = %d, now = GSS_S_NO_CRED\n",
 		   major_status);
-
+	   acquire_fail = 1;
 	   major_status = GSS_S_NO_CRED;
 	   goto fail;
        }
@@ -543,6 +544,7 @@
 
    if ((code = krb5_auth_con_init(context, &auth_context))) {
        major_status = GSS_S_FAILURE;
+       save_error_info((OM_uint32)code, context);
        /* Solaris Kerberos */
        KRB5_LOG(KRB5_ERR, "krb5_gss_accept_sec_context() "
 	      "krb5_auth_con_init() error code %d", code);
@@ -572,9 +574,29 @@
 
    if ((code = krb5_rd_req_decoded(context, &auth_context, request,
 			   cred->princ, cred->keytab, NULL, &ticket))) {
-      KRB5_LOG(KRB5_ERR, "krb5_gss_accept_sec_context() "
+       KRB5_LOG(KRB5_ERR, "krb5_gss_accept_sec_context() "
 	      "krb5_rd_req() error code %d", code);
-       if (code == KRB5_KT_KVNONOTFOUND || code == KRB5_KT_NOTFOUND) {
+       if (code == KRB5_KT_KVNONOTFOUND) {
+	   char *s_name;
+	   if (krb5_unparse_name(context, cred->princ, &s_name) == 0) {	
+	       krb5_set_error_message(context, KRB5KRB_AP_ERR_BADKEYVER,
+				    dgettext(TEXT_DOMAIN,
+					    "Key version %d is not available for principal %s"),
+				    request->ticket->enc_part.kvno,
+				    s_name);
+	       krb5_free_unparsed_name(context, s_name);
+	   }
+	   major_status = GSS_S_DEFECTIVE_CREDENTIAL;
+	   code = KRB5KRB_AP_ERR_BADKEYVER;
+       } else if (code == KRB5_KT_NOTFOUND) {
+	   char *s_name;
+	   if (krb5_unparse_name(context, cred->princ, &s_name) == 0) {	
+	       krb5_set_error_message(context, KRB5KRB_AP_ERR_NOKEY,
+				    dgettext(TEXT_DOMAIN,
+					    "Service key %s not available"),
+				    s_name);
+	       krb5_free_unparsed_name(context, s_name);
+	   }
            major_status = GSS_S_DEFECTIVE_CREDENTIAL;
 	   code = KRB5KRB_AP_ERR_NOKEY;
        }
@@ -1076,21 +1098,24 @@
 
    /* set the return arguments */
 
-   if (src_name) {
-       if ((code = krb5_copy_principal(context, ctx->there, &name))) {
-	   major_status = GSS_S_FAILURE;
-	   goto fail;
-       }
-       /* intern the src_name */
-       if (! kg_save_name((gss_name_t) name)) {
-	   code = G_VALIDATE_FAILED;
-	   major_status = GSS_S_FAILURE;
-	   goto fail;
-       }
+   /*
+    * Solaris Kerberos
+    * Regardless of src_name, get name for error msg if neeeded.
+    */
+   if ((code = krb5_copy_principal(context, ctx->there, &client_name))) {
+	major_status = GSS_S_FAILURE;
+	goto fail;
    }
-
-   if (mech_type)
-      *mech_type = (gss_OID) mech_used;
+   if ((code = krb5_copy_principal(context, ctx->here, &server_name))) {
+	major_status = GSS_S_FAILURE;
+	goto fail;
+   }
+   /* intern the src_name */
+   if (! kg_save_name((gss_name_t) client_name)) {
+	code = G_VALIDATE_FAILED;
+	major_status = GSS_S_FAILURE;
+	goto fail;
+   }
 
    if (time_rec)
       *time_rec = ctx->endtime - now;
@@ -1102,7 +1127,7 @@
    *output_token = token;
 
    if (src_name)
-      *src_name = (gss_name_t) name;
+      *src_name = (gss_name_t) client_name;
 
    if (delegated_cred_handle && deleg_cred) {
        if (!kg_save_cred_id((gss_cred_id_t) deleg_cred)) {
@@ -1123,6 +1148,21 @@
    major_status = GSS_S_COMPLETE;
 
  fail:
+   if (mech_type) {
+	unsigned int min;
+	gss_buffer_desc oidstr;
+	oidstr.value = NULL;
+
+	/*
+	 * This needs to be set/returned even on fail so
+	 * gss_accept_sec_context() can map_error_oid() the correct
+	 * error/oid for later use by gss_display_status().
+	 * (needed in CIFS/SPNEGO case)
+	 */
+	*mech_type = (gss_OID) mech_used;
+
+	(void) gss_oid_to_str(&min, *mech_type, &oidstr);
+   }
 
    if (authdat)
        krb5_free_authenticator(context, authdat);
@@ -1169,10 +1209,6 @@
    }
    if (token.value)
        xfree(token.value);
-   if (name) {
-       (void) kg_delete_name((gss_name_t) name);
-       krb5_free_principal(context, name);
-   }
 
    *minor_status = code;
 
@@ -1219,7 +1255,7 @@
 		    krb_error_data.e_data.length = len;
 	    }
 	    major_status = GSS_S_CONTINUE_NEEDED;
-	}
+       }
 
        code -= ERROR_TABLE_BASE_krb5;
        if (code < 0 || code > 128)
@@ -1252,7 +1288,6 @@
    }
 
 cleanup:
-
    /* Solaris Kerberos */
    if (krb_error_data.e_data.data != NULL)
         free(krb_error_data.e_data.data);
@@ -1260,6 +1295,45 @@
    if (!verifier_cred_handle && cred_handle) {
 	krb5_gss_release_cred(&t_minor_status, &cred_handle);
    }
+
+   /*
+    * Solaris Kerberos
+    * Enhance the error message.
+    */
+   if (GSS_ERROR(major_status)) {
+       if (client_name && server_name &&
+	 (*minor_status == (OM_uint32)KRB5KRB_AP_ERR_BAD_INTEGRITY)) {
+	    char *c_name = NULL;
+	    char *s_name = NULL;
+	    krb5_error_code cret, sret;
+	    cret = krb5_unparse_name(context, (krb5_principal) client_name,
+				    &c_name);
+	    sret = krb5_unparse_name(context, (krb5_principal) server_name,
+				    &s_name);
+	    krb5_set_error_message(context, *minor_status,
+				dgettext(TEXT_DOMAIN,
+					"Decrypt integrity check failed for client '%s' and server '%s'"),
+				cret == 0 ? c_name : "unknown",
+				sret == 0 ? s_name : "unknown");
+	    if (s_name)
+		    krb5_free_unparsed_name(context, s_name);
+	    if (c_name)
+		    krb5_free_unparsed_name(context, c_name);
+	    }
+       /*
+	* Solaris Kerberos
+	* krb5_gss_acquire_cred() does not take a context arg
+	* (and does a save_error_info() itself) so re-calling
+	* save_error_info() here is trouble.
+	*/
+       if (!acquire_fail)
+	    save_error_info(*minor_status, context);
+   }
+   if (client_name) {
+	(void) kg_delete_name((gss_name_t) client_name);
+   }
+   if (server_name)
+	krb5_free_principal(context, server_name);
    krb5_free_context(context);
 
    /* Solaris Kerberos */
--- a/usr/src/lib/gss_mechs/mech_krb5/mech/acquire_cred.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/gss_mechs/mech_krb5/mech/acquire_cred.c	Mon Aug 16 17:01:32 2010 -0700
@@ -1,9 +1,6 @@
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
-
-
 /*
  * Copyright 2000 by the Massachusetts Institute of Technology.
  * All Rights Reserved.
@@ -85,6 +82,9 @@
 #else
 #include <strings.h>
 #endif
+#include <syslog.h>
+#include <locale.h> /* Solaris Kerberos */
+#include "file/ktfile.h" /* Solaris Kerberos */
 
 #if defined(USE_LOGIN_LIBRARY)
 #include <Kerberos/KerberosLoginPrivate.h>
@@ -184,18 +184,27 @@
       return(GSS_S_NO_CRED);
    }
 
-   if (desired_name != GSS_C_NO_NAME) {
-      princ = (krb5_principal) desired_name;
-      if ((code = krb5_kt_get_entry(context, kt, princ, 0, 0, &entry))) {
-	 (void) krb5_kt_close(context, kt);
-	 if (code == KRB5_KT_NOTFOUND)
-	    *minor_status = KG_KEYTAB_NOMATCH;
-	 else
-	    *minor_status = code;
+    if (desired_name != GSS_C_NO_NAME) {
+        princ = (krb5_principal) desired_name;
+        if ((code = krb5_kt_get_entry(context, kt, princ, 0, 0, &entry))) {
+	    if (code == KRB5_KT_NOTFOUND) {
+	        char *s_name;
+	        if (krb5_unparse_name(context, princ, &s_name) == 0) {
+		    krb5_set_error_message(context, KG_KEYTAB_NOMATCH,
+					dgettext(TEXT_DOMAIN,
+						"No principal in keytab ('%s') matches desired name %s"),
+					KTFILENAME(kt),
+					s_name);
+	            krb5_free_unparsed_name(context, s_name);
+		}
+		*minor_status = KG_KEYTAB_NOMATCH;
+	    } else
+	        *minor_status = code;
 	 /* Solaris Kerb NOTE: GSS_S_CRED_UNAVAIL is not RFC 2743 compliant */
-	 return(GSS_S_NO_CRED);
-      }
-      krb5_kt_free_entry(context, &entry);
+	    (void) krb5_kt_close(context, kt);
+	    return(GSS_S_NO_CRED);
+	}
+	krb5_kt_free_entry(context, &entry);
 
       /* Open the replay cache for this principal. */
       if ((code = krb5_get_server_rcache(context,
@@ -205,7 +214,7 @@
 	 return(GSS_S_FAILURE);
       }
 
-   }
+    }
 
 /* hooray.  we made it */
 
@@ -491,9 +500,9 @@
    /*SUPPRESS 29*/
    if ((desired_name != (gss_name_t) NULL) &&
        (! kg_validate_name(desired_name))) {
-      *minor_status = (OM_uint32) G_VALIDATE_FAILED;
-      krb5_free_context(context);
-      return(GSS_S_CALL_BAD_STRUCTURE|GSS_S_BAD_NAME);
+	*minor_status = (OM_uint32) G_VALIDATE_FAILED;	
+	krb5_free_context(context);
+	return(GSS_S_CALL_BAD_STRUCTURE|GSS_S_BAD_NAME);
    }
 
    /* verify that the requested mechanism set is the default, or
@@ -571,6 +580,7 @@
          k5_mutex_destroy(&cred->lock);
          xfree(cred);
 	 /* minor_status set by acquire_accept_cred() */
+	 save_error_info(*minor_status, context);
 	 krb5_free_context(context);
 	 return(ret);
       }
@@ -593,6 +603,7 @@
          k5_mutex_destroy(&cred->lock);
          xfree(cred);
 	 /* minor_status set by acquire_init_cred() */
+         save_error_info(*minor_status, context);
 	 krb5_free_context(context);
 	 return(ret);
       }
@@ -613,6 +624,7 @@
          k5_mutex_destroy(&cred->lock);
          xfree(cred);
 	 *minor_status = code;
+         save_error_info(*minor_status, context);
 	 krb5_free_context(context);
 	 return(GSS_S_FAILURE);
       }
@@ -637,6 +649,7 @@
          k5_mutex_destroy(&cred->lock);
          xfree(cred);
 	 *minor_status = code;
+	 save_error_info(*minor_status, context);
 	 krb5_free_context(context);
 	 return(GSS_S_FAILURE);
       }
@@ -686,6 +699,7 @@
       k5_mutex_destroy(&cred->lock);
       xfree(cred);
       *minor_status = (OM_uint32) G_VALIDATE_FAILED;
+      save_error_string(*minor_status, "error saving credentials");
       krb5_free_context(context);
       return(GSS_S_FAILURE);
    }
--- a/usr/src/lib/gss_mechs/mech_krb5/mech/add_cred.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/gss_mechs/mech_krb5/mech/add_cred.c	Mon Aug 16 17:01:32 2010 -0700
@@ -1,3 +1,6 @@
+/*
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
+ */
 /*
  * Copyright 2000 by the Massachusetts Institute of Technology.
  * All Rights Reserved.
@@ -54,6 +57,7 @@
 #else
 #include <strings.h>
 #endif
+#include <locale.h> /* Solaris Kerberos */
 
 /*
  * $Id: add_cred.c 18396 2006-07-25 20:29:43Z lxs $
@@ -122,6 +126,7 @@
     major_status = krb5_gss_validate_cred_1(minor_status, input_cred_handle,
 					    context);
     if (GSS_ERROR(major_status)) {
+        save_error_info(*minor_status, context);
 	krb5_free_context(context);
 	return major_status;
     }
@@ -150,6 +155,7 @@
     }
 
     if (GSS_ERROR(kg_sync_ccache_name(context, minor_status))) {
+        save_error_info(*minor_status, context);
 	krb5_free_context(context);
 	return GSS_S_FAILURE;
     }
@@ -159,7 +165,7 @@
     /*SUPPRESS 29*/
     if ((desired_name != (gss_name_t) NULL) &&
 	(! kg_validate_name(desired_name))) {
-	*minor_status = (OM_uint32) G_VALIDATE_FAILED;
+	*minor_status = (OM_uint32) G_VALIDATE_FAILED;	
 	krb5_free_context(context);
 	return(GSS_S_CALL_BAD_STRUCTURE|GSS_S_BAD_NAME);
     }
@@ -169,7 +175,24 @@
     if (desired_name &&
 	!krb5_principal_compare(context, (krb5_principal) desired_name,
 				cred->princ)) {
-	*minor_status = 0;
+        /* Solaris Kerberos: spruce-up the err msg */
+        krb5_principal dname = (krb5_principal) desired_name;
+	char *s_name = NULL, *s_princ= NULL;
+	int kret = krb5_unparse_name(context, dname, &s_name);
+	int kret1 = krb5_unparse_name(context, cred->princ, &s_princ);
+	*minor_status = (OM_uint32) G_BAD_USAGE;
+	if (kret == 0 && kret1 == 0) {
+	    krb5_set_error_message(context, *minor_status,
+				dgettext(TEXT_DOMAIN,
+					"Desired name principal '%s' does not match '%s'"),
+				s_name, s_princ);
+	    save_error_info(*minor_status, context);
+	}
+	if (s_name)
+	    krb5_free_unparsed_name(context, s_name);
+	if (s_princ)
+	    krb5_free_unparsed_name(context, s_princ);
+
 	krb5_free_context(context);
 	return(GSS_S_BAD_NAME);
     }
@@ -203,6 +226,7 @@
 	    xfree(new_cred);
 
 	    *minor_status = code;
+	    save_error_info(*minor_status, context);
 	    krb5_free_context(context);
 	    return(GSS_S_FAILURE);
 	}
@@ -232,6 +256,7 @@
 		xfree(new_cred);
 
 		*minor_status = code;
+		save_error_info(*minor_status, context);
 		krb5_free_context(context);
 		return(GSS_S_FAILURE);
 	    }
@@ -243,6 +268,7 @@
 		xfree(new_cred);
 
 		*minor_status = code;
+		save_error_info(*minor_status, context);
 		krb5_free_context(context);
 		return(GSS_S_FAILURE);
 	    }
@@ -261,8 +287,9 @@
 		    krb5_free_principal(context, new_cred->princ);
 		xfree(new_cred);
 
+		*minor_status = code;
+		save_error_info(*minor_status, context);
 		krb5_free_context(context);
-		*minor_status = code;
 		return(GSS_S_FAILURE);
 	    }
 	} else {
@@ -301,9 +328,9 @@
 		if (new_cred->princ)
 		    krb5_free_principal(context, new_cred->princ);
 		xfree(new_cred);
+		*minor_status = code;
+		save_error_info(*minor_status, context);
 		krb5_free_context(context);
-
-		*minor_status = code;
 		return(GSS_S_FAILURE);
 	    }
 	} else {
--- a/usr/src/lib/gss_mechs/mech_krb5/mech/context_time.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/gss_mechs/mech_krb5/mech/context_time.c	Mon Aug 16 17:01:32 2010 -0700
@@ -1,5 +1,6 @@
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
+/*
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
+ */
 /* Copyright 1993 by OpenVision Technologies, Inc.
  * 
  * Permission to use, copy, modify, distribute, and sell this software
@@ -53,6 +54,7 @@
 
    if ((code = krb5_timeofday(ctx->k5_context, &now))) {
       *minor_status = code;
+      save_error_info(*minor_status, ctx->k5_context);
       return(GSS_S_FAILURE);
    }
 
--- a/usr/src/lib/gss_mechs/mech_krb5/mech/copy_ccache.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/gss_mechs/mech_krb5/mech/copy_ccache.c	Mon Aug 16 17:01:32 2010 -0700
@@ -1,4 +1,6 @@
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
+/*
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
+ */
 
 #include "gssapiP_krb5.h"
 
@@ -43,6 +45,7 @@
    if (code) {
        k5_mutex_unlock(&k5creds->lock);
        *minor_status = code;
+       save_error_info(*minor_status, context);
        krb5_free_context(context);
        return(GSS_S_FAILURE);
    }
@@ -53,6 +56,7 @@
    krb5_free_context(context);
    if (code) {
        *minor_status = code;
+       save_error_info(*minor_status, context);
        return(GSS_S_FAILURE);
    } else {
        *minor_status = 0;
--- a/usr/src/lib/gss_mechs/mech_krb5/mech/disp_com_err_status.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/gss_mechs/mech_krb5/mech/disp_com_err_status.c	Mon Aug 16 17:01:32 2010 -0700
@@ -25,7 +25,6 @@
  */
 
 #include "gssapiP_generic.h"
-#include "gss_libinit.h"
 #include "com_err.h"
 
 /* XXXX internationalization!! */
@@ -53,11 +52,9 @@
    status_string->length = 0;
    status_string->value = NULL;
 
-   (void) gssint_initialize_library();
-
    if (! g_make_string_buffer(((status_value == 0)?no_error:
-			       error_message(status_value)),
-			      status_string)) {
+			    error_message(status_value)),
+			    status_string)) {
       *minor_status = ENOMEM;
       return(GSS_S_FAILURE);
    }
--- a/usr/src/lib/gss_mechs/mech_krb5/mech/disp_name.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/gss_mechs/mech_krb5/mech/disp_name.c	Mon Aug 16 17:01:32 2010 -0700
@@ -1,5 +1,3 @@
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 /*
  * Copyright 1993 by OpenVision Technologies, Inc.
  * 
@@ -54,6 +52,7 @@
    if ((code = krb5_unparse_name(context,
 				 (krb5_principal) input_name, &str))) {
       *minor_status = code;
+      save_error_info(*minor_status, context);
       krb5_free_context(context);
       return(GSS_S_FAILURE);
    }
--- a/usr/src/lib/gss_mechs/mech_krb5/mech/disp_status.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/gss_mechs/mech_krb5/mech/disp_status.c	Mon Aug 16 17:01:32 2010 -0700
@@ -1,6 +1,10 @@
+/* -*- mode: c; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
+ */
 /*
  * Copyright 1993 by OpenVision Technologies, Inc.
- * 
+ *
  * Permission to use, copy, modify, distribute, and sell this software
  * and its documentation for any purpose is hereby granted without fee,
  * provided that the above copyright notice appears in all copies and
@@ -10,7 +14,7 @@
  * without specific, written prior permission. OpenVision makes no
  * representations about the suitability of this software for any
  * purpose.  It is provided "as is" without express or implied warranty.
- * 
+ *
  * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
  * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
@@ -20,51 +24,210 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
+#include "gssapiP_krb5.h"
+#include "com_err.h"
+#include <syslog.h>
+/* XXXX internationalization!! */
+
+static inline int
+compare_OM_uint32 (OM_uint32 a, OM_uint32 b)
+{
+    if (a < b)
+        return -1;
+    else if (a == b)
+        return 0;
+    else
+        return 1;
+}
+static inline void
+free_string (char *s)
+{
+    free(s);
+}
+#include "error_map.h"
+#include <stdio.h>
+/*
+ * AKA krb5_gss_get_error_message.  See #define in gssapiP_krb5.h.
+ */
+char *get_error_message(OM_uint32 minor_code)
+{
+    gsserrmap *p = k5_getspecific(K5_KEY_GSS_KRB5_ERROR_MESSAGE);
+    char *msg = NULL;
+
+#ifdef DEBUG
+    fprintf(stderr, "%s(%lu, p=%p)", __func__, (unsigned long) minor_code,
+            (void *) p);
+#endif
+    if (p) {
+        char **v = gsserrmap_find(p, minor_code);
+        if (v) {
+            msg = *v;
+#ifdef DEBUG
+            fprintf(stderr, " FOUND!");
+#endif
+        }
+    }
+    if (msg == NULL)
+        msg = (char *)error_message((krb5_error_code)minor_code);
+#ifdef DEBUG
+    fprintf(stderr, " -> %p/%s\n", (void *) msg, msg);
+#endif
+
+    return msg;
+}
+#define save_error_string_nocopy gss_krb5_save_error_string_nocopy
+static int save_error_string_nocopy(OM_uint32 minor_code, char *msg)
+{
+    gsserrmap *p;
+    int ret;
 
-#include "gssapiP_krb5.h"
-#include "gss_libinit.h"
-#include "com_err.h"
+#ifdef DEBUG
+    fprintf(stderr, "%s(%lu, %s)", __func__, (unsigned long) minor_code, msg);
+#endif
+    p = k5_getspecific(K5_KEY_GSS_KRB5_ERROR_MESSAGE);
+    if (!p) {
+        p = malloc(sizeof(*p));
+        if (p == NULL) {
+            ret = 1;
+            goto fail;
+        }
+        if (gsserrmap_init(p) != 0) {
+            free(p);
+            p = NULL;
+            ret = 1;
+            goto fail;
+        }
+        if (k5_setspecific(K5_KEY_GSS_KRB5_ERROR_MESSAGE, p) != 0) {
+            gsserrmap_destroy(p);
+            free(p);
+            p = NULL;
+            ret = 1;
+            goto fail;
+        }
+    }
+    ret = gsserrmap_replace_or_insert(p, minor_code, msg);
+    /* Solaris Kerberos */
+    if (ret) {
+            gsserrmap_destroy(p);
+            free(p);
+            p = NULL;
+    }
 
-/* XXXX internationalization!! */
+fail:
+#ifdef DEBUG
+    fprintf(stderr, " p=%p %s\n", (void *)p, ret ? "FAIL" : "SUCCESS");
+#endif
+    return ret;
+}
+void save_error_string(OM_uint32 minor_code, char *msg)
+{
+    char *s = strdup(msg);
+    if (s) {
+        if (save_error_string_nocopy(minor_code, s) != 0)
+            free(s);
+    }
+}
+void save_error_message(OM_uint32 minor_code, const char *format, ...)
+{
+    char *s;
+    int n;
+    va_list ap;
+
+    va_start(ap, format);
+    n = vasprintf(&s, format, ap);
+    va_end(ap);
+    if (n >= 0) {
+        if (save_error_string_nocopy(minor_code, s) != 0)
+            free(s);
+    }
+}
+void krb5_gss_save_error_info(OM_uint32 minor_code, krb5_context ctx)
+{
+    char *s;
+
+#ifdef DEBUG
+    fprintf(stderr, "%s(%lu, ctx=%p)\n", __func__,
+            (unsigned long) minor_code, (void *)ctx);
+#endif
+    s = (char *)krb5_get_error_message(ctx, (krb5_error_code)minor_code);
+#ifdef DEBUG
+    fprintf(stderr, "%s(%lu, ctx=%p) saving: %s\n", __func__,
+            (unsigned long) minor_code, (void *)ctx, s);
+#endif
+    save_error_string(minor_code, s);
+    /* The get_error_message call above resets the error message in
+       ctx.  Put it back, in case we make this call again *sigh*.  */
+    krb5_set_error_message(ctx, (krb5_error_code)minor_code, "%s", s);
+    krb5_free_error_message(ctx, s);
+}
+void krb5_gss_delete_error_info(void *p)
+{
+    gsserrmap_destroy(p);
+}
 
 /**/
 
 OM_uint32
 krb5_gss_display_status(minor_status, status_value, status_type,
-			mech_type, message_context, status_string)
-     OM_uint32 *minor_status;
-     OM_uint32 status_value;
-     int status_type;
-     gss_OID mech_type;
-     OM_uint32 *message_context;
-     gss_buffer_t status_string;
+                        mech_type, message_context, status_string)
+    OM_uint32 *minor_status;
+    OM_uint32 status_value;
+    int status_type;
+    gss_OID mech_type;
+    OM_uint32 *message_context;
+    gss_buffer_t status_string;
 {
-   status_string->length = 0;
-   status_string->value = NULL;
+    status_string->length = 0;
+    status_string->value = NULL;
 
-   if ((mech_type != GSS_C_NULL_OID) &&
-       !g_OID_equal(gss_mech_krb5, mech_type) &&
-       !g_OID_equal(gss_mech_krb5_old, mech_type)) {
-       *minor_status = 0;
-       return(GSS_S_BAD_MECH);
+    if ((mech_type != GSS_C_NULL_OID) &&
+        !g_OID_equal(gss_mech_krb5, mech_type) &&
+        !g_OID_equal(gss_mech_krb5_old, mech_type)) {
+        *minor_status = 0;
+        return(GSS_S_BAD_MECH);
     }
 
-   if (status_type == GSS_C_GSS_CODE) {
-      return(g_display_major_status(minor_status, status_value,
-				    message_context, status_string));
-   } else if (status_type == GSS_C_MECH_CODE) {
-      (void) gssint_initialize_library();
+    if (status_type == GSS_C_GSS_CODE) {
+        return(g_display_major_status(minor_status, status_value,
+                                      message_context, status_string));
+    } else if (status_type == GSS_C_MECH_CODE) {
+        (void) gss_krb5int_initialize_library();
+
+        if (*message_context) {
+            *minor_status = (OM_uint32) G_BAD_MSG_CTX;
+            return(GSS_S_FAILURE);
+        }
 
-      if (*message_context) {
-	 *minor_status = (OM_uint32) G_BAD_MSG_CTX;
-	 return(GSS_S_FAILURE);
-      }
+        /* If this fails, there's not much we can do...  */
+        /* Solaris Kerberos - cleaned-up/fixed the return checks/values here */
+        if (!g_make_string_buffer(krb5_gss_get_error_message(status_value),
+                                 status_string)) {
+            *minor_status = ENOMEM;
+            return(GSS_S_FAILURE);
+        }
+        *minor_status = 0;
+        return(GSS_S_COMPLETE);
+    } else {
+        *minor_status = 0;
+        return(GSS_S_BAD_STATUS);
+    }
+}
 
-      return(g_display_com_err_status(minor_status, status_value,
-				      status_string));
-   } else {
-      *minor_status = 0;
-      return(GSS_S_BAD_STATUS);
-   }
+/*
+ * Solaris Kerberos
+ * Hack alert: workaround obfusicated func name issues for mech_spnego.so.
+ */
+OM_uint32
+krb5_gss_display_status2(minor_status, status_value, status_type,
+                        mech_type, message_context, status_string)
+    OM_uint32 *minor_status;
+    OM_uint32 status_value;
+    int status_type;
+    gss_OID mech_type;
+    OM_uint32 *message_context;
+    gss_buffer_t status_string;
+{
+        return(krb5_gss_display_status(minor_status, status_value,
+ 				  status_type, mech_type, message_context,
+ 				  status_string));
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/gss_mechs/mech_krb5/mech/errmap.h	Mon Aug 16 17:01:32 2010 -0700
@@ -0,0 +1,276 @@
+/*
+ * This file is generated, please don't edit it.
+ * script: ./../../../util/gen.pl
+ * args:   bimap errmap.h NAME=mecherrmap LEFT=OM_uint32 RIGHT=struct mecherror LEFTPRINT=print_OM_uint32 RIGHTPRINT=mecherror_print LEFTCMP=cmp_OM_uint32 RIGHTCMP=mecherror_cmp
+ * The rest of this file is copied from a template, with
+ * substitutions.  See the template for copyright info.
+ */
+/* start of t_bimap header template */
+/*
+ * bidirectional mapping table, add-only
+ *
+ * Parameters:
+ * NAME
+ * LEFT, RIGHT - types
+ * LEFTCMP, RIGHTCMP - comparison functions
+ *
+ * Methods:
+ * int init() - nonzero is error code, if any possible
+ * long size()
+ * void foreach(int (*)(LEFT, RIGHT, void*), void*)
+ * int add(LEFT, RIGHT) - 0 = success, -1 = allocation failure
+ * const struct mecherror *findleft(OM_uint32) - null iff not found
+ * const OM_uint32 *findright(struct mecherror)
+ * void destroy() - destroys container, doesn't delete elements
+ *
+ * initial implementation: flat array of (left,right) pairs
+ */
+
+struct mecherrmap__pair {
+    OM_uint32 l;
+    struct mecherror r;
+};
+/* end of t_bimap header template */
+/* start of t_array template */
+
+/*
+ * array type, derived from template
+ *
+ * parameters:
+ * NAME: mecherrmap__pairarray
+ * TYPE: struct mecherrmap__pair
+ *
+ * methods:
+ * int init() -> nonzero if fail initial allocation
+ * unsigned long size() -> nonnegative number of values stored
+ * int grow(newsize) -> negative if fail allocation, memset(,0,) new space
+ * struct mecherrmap__pair *getaddr(idx) -> aborts if out of range
+ * void set(idx, value) -> aborts if out of range
+ * struct mecherrmap__pair get(idx) -> value, or aborts if out of range
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <limits.h>
+#include <string.h>
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+
+struct mecherrmap__pairarray__header {
+    size_t allocated;
+    struct mecherrmap__pair *elts;
+};
+typedef struct mecherrmap__pairarray__header mecherrmap__pairarray;
+
+static inline int
+mecherrmap__pairarray_init(mecherrmap__pairarray *arr)
+{
+    arr->elts = calloc(10, sizeof(struct mecherrmap__pair));
+    if (arr->elts == NULL)
+	return ENOMEM;
+    arr->allocated = 10;
+    return 0;
+}
+
+static inline long
+mecherrmap__pairarray_size(mecherrmap__pairarray *arr)
+{
+    return arr->allocated;
+}
+
+static inline long
+mecherrmap__pairarray_max_size(mecherrmap__pairarray *arr)
+{
+    size_t upper_bound;
+
+    upper_bound = SIZE_MAX / sizeof(*arr->elts);
+    if (upper_bound > LONG_MAX)
+	upper_bound = LONG_MAX;
+    return (long) upper_bound;
+}
+
+static inline int
+mecherrmap__pairarray_grow(mecherrmap__pairarray *arr, unsigned long newcount)
+{
+    size_t oldsize = sizeof(*arr->elts) * arr->allocated;
+    size_t newsize;
+    void *ptr;
+
+    if (newcount > LONG_MAX)
+	return -1;
+    if (newcount < arr->allocated)
+	return 0;
+    if (newcount > mecherrmap__pairarray_max_size(arr))
+	return -1;
+
+    newsize = sizeof(*arr->elts) * newcount;
+    ptr = realloc(arr->elts, newsize);
+    if (ptr == NULL)
+	return -1;
+    memset((char *)ptr + oldsize, 0, newsize - oldsize);
+    arr->elts = ptr;
+    arr->allocated = newcount;
+    return 0;
+}
+
+static inline struct mecherrmap__pair *
+mecherrmap__pairarray_getaddr (mecherrmap__pairarray *arr, long idx)
+{
+    if (idx < 0 || idx >= arr->allocated)
+	abort();
+    return arr->elts + idx;
+}
+
+static inline void
+mecherrmap__pairarray_set (mecherrmap__pairarray *arr, long idx, struct mecherrmap__pair value)
+{
+    struct mecherrmap__pair *newvalp;
+    newvalp = mecherrmap__pairarray_getaddr(arr, idx);
+    *newvalp = value;
+}
+
+static inline struct mecherrmap__pair
+mecherrmap__pairarray_get (mecherrmap__pairarray *arr, long idx)
+{
+    return *mecherrmap__pairarray_getaddr(arr, idx);
+}
+
+static inline void
+mecherrmap__pairarray_destroy (mecherrmap__pairarray *arr)
+{
+    free(arr->elts);
+    arr->elts = 0;
+}
+/* end of t_array template */
+/* start of t_bimap body template */
+
+/* for use in cases where text substitutions may not work, like putting
+   "const" before a type that turns out to be "char *"  */
+typedef OM_uint32 mecherrmap__left_t;
+typedef struct mecherror mecherrmap__right_t;
+
+typedef struct {
+    mecherrmap__pairarray a;
+    long nextidx;
+} mecherrmap;
+
+static inline int
+mecherrmap_init (mecherrmap *m)
+{
+    m->nextidx = 0;
+    return mecherrmap__pairarray_init (&m->a);
+}
+
+static inline long
+mecherrmap_size (mecherrmap *m)
+{
+    return mecherrmap__pairarray_size (&m->a);
+}
+
+static inline void
+mecherrmap_foreach (mecherrmap *m, int (*fn)(OM_uint32, struct mecherror, void *), void *p)
+{
+    long i, sz;
+    sz = m->nextidx;
+    for (i = 0; i < sz; i++) {
+	struct mecherrmap__pair *pair;
+	pair = mecherrmap__pairarray_getaddr (&m->a, i);
+	if ((*fn)(pair->l, pair->r, p) != 0)
+	    break;
+    }
+}
+
+static inline int
+mecherrmap_add (mecherrmap *m, OM_uint32 l, struct mecherror r)
+{
+    long i, sz;
+    struct mecherrmap__pair newpair;
+    int err;
+
+    sz = m->nextidx;
+    /* Make sure we're not duplicating.  */
+    for (i = 0; i < sz; i++) {
+	struct mecherrmap__pair *pair;
+	pair = mecherrmap__pairarray_getaddr (&m->a, i);
+	assert ((*cmp_OM_uint32)(l, pair->l) != 0);
+	if ((*cmp_OM_uint32)(l, pair->l) == 0)
+	    abort();
+	assert ((*mecherror_cmp)(r, pair->r) != 0);
+	if ((*mecherror_cmp)(r, pair->r) == 0)
+	    abort();
+    }
+    newpair.l = l;
+    newpair.r = r;
+    if (sz >= LONG_MAX - 1)
+	return ENOMEM;
+    err = mecherrmap__pairarray_grow (&m->a, sz+1);
+    if (err)
+	return err;
+    mecherrmap__pairarray_set (&m->a, sz, newpair);
+    m->nextidx++;
+    return 0;
+}
+
+static inline const mecherrmap__right_t *
+mecherrmap_findleft (mecherrmap *m, OM_uint32 l)
+{
+    long i, sz;
+    sz = mecherrmap_size (m);
+    for (i = 0; i < sz; i++) {
+	struct mecherrmap__pair *pair;
+	pair = mecherrmap__pairarray_getaddr (&m->a, i);
+	if ((*cmp_OM_uint32)(l, pair->l) == 0)
+	    return &pair->r;
+    }
+    return 0;
+}
+
+static inline const mecherrmap__left_t *
+mecherrmap_findright (mecherrmap *m, struct mecherror r)
+{
+    long i, sz;
+    sz = mecherrmap_size (m);
+    for (i = 0; i < sz; i++) {
+	struct mecherrmap__pair *pair;
+	pair = mecherrmap__pairarray_getaddr (&m->a, i);
+	if ((*mecherror_cmp)(r, pair->r) == 0)
+	    return &pair->l;
+    }
+    return 0;
+}
+
+struct mecherrmap__printstat {
+    FILE *f;
+    int comma;
+};
+static inline int
+mecherrmap__printone (OM_uint32 l, struct mecherror r, void *p)
+{
+    struct mecherrmap__printstat *ps = p;
+    fprintf(ps->f, ps->comma ? ", (" : "(");
+    ps->comma = 1;
+    (*print_OM_uint32)(l, ps->f);
+    fprintf(ps->f, ",");
+    (*mecherror_print)(r, ps->f);
+    fprintf(ps->f, ")");
+    return 0;
+}
+
+static inline void
+mecherrmap_printmap (mecherrmap *m, FILE *f)
+{
+    struct mecherrmap__printstat ps;
+    ps.comma = 0;
+    ps.f = f;
+    fprintf(f, "(");
+    mecherrmap_foreach (m, mecherrmap__printone, &ps);
+    fprintf(f, ")");
+}
+
+static inline void
+mecherrmap_destroy (mecherrmap *m)
+{
+    mecherrmap__pairarray_destroy (&m->a);
+}
+/* end of t_bimap body template */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/gss_mechs/mech_krb5/mech/error_map.h	Mon Aug 16 17:01:32 2010 -0700
@@ -0,0 +1,113 @@
+/*   Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. */
+/*
+ * This file is generated, please don't edit it.
+ * script: ./../../../util/gen-map.pl
+ * args:
+ *	-oerror_map.new
+ *	NAME=gsserrmap
+ *	KEY=OM_uint32
+ *	VALUE=char *
+ *	COMPARE=compare_OM_uint32
+ *	FREEVALUE=free_string
+ * The rest of this file is copied from a template, with
+ * substitutions.  See the template for copyright info.
+ */
+/*
+ * map, generated from template
+ * map name: gsserrmap
+ * key: OM_uint32
+ * value: char *
+ * compare: compare_OM_uint32
+ * copy_key: 0
+ * free_key: 0
+ * free_value: free_string
+ */
+struct gsserrmap__element {
+    OM_uint32 key;
+    char * value;
+    struct gsserrmap__element *next;
+};
+struct gsserrmap__head {
+    struct gsserrmap__element *first;
+};
+typedef struct gsserrmap__head gsserrmap;
+static inline int gsserrmap_init (struct gsserrmap__head *head)
+{
+    head->first = NULL;
+    return 0;
+}
+static inline void gsserrmap_destroy (struct gsserrmap__head *head)
+{
+    struct gsserrmap__element *e, *e_next;
+    void (*free_key)(OM_uint32) = 0;
+    void (*free_value)(char *) = free_string;
+    for (e = head->first; e; e = e_next) {
+	e_next = e->next;
+	if (free_key)
+	    (*free_key)(e->key);
+	if (free_value)
+	    (*free_value)(e->value);
+	free(e);
+    }
+    head->first = NULL;
+}
+/* Returns pointer to linked-list entry, or null if key not found.  */
+static inline struct gsserrmap__element *
+gsserrmap__find_node (struct gsserrmap__head *head, OM_uint32 key)
+{
+    struct gsserrmap__element *e;
+    for (e = head->first; e; e = e->next)
+	if (compare_OM_uint32 (key, e->key) == 0)
+	    return e;
+    return 0;
+}
+/* Returns pointer to value, or null if key not found.  */
+static inline char * *
+gsserrmap_find (struct gsserrmap__head *head, OM_uint32 key)
+{
+    struct gsserrmap__element *e = gsserrmap__find_node(head, key);
+    if (e)
+	return &e->value;
+    return 0;
+}
+/* Returns 0 or error code.  */
+static inline int
+gsserrmap__copy_key (OM_uint32 *out, OM_uint32 in)
+{
+    int (*copykey)(OM_uint32 *, OM_uint32) = 0;
+    if (copykey == 0) {
+	*out = in;
+	return 0;
+    } else
+	return (*copykey)(out, in);
+}
+/* Returns 0 or error code.  */
+static inline int
+gsserrmap_replace_or_insert (struct gsserrmap__head *head,
+			  OM_uint32 key, char * new_value)
+{
+    struct gsserrmap__element *e = gsserrmap__find_node(head, key);
+    int ret;
+
+    if (e) {
+	/* replace */
+	void (*free_value)(char *) = free_string;
+	if (free_value)
+	    (*free_value)(e->value);
+	e->value = new_value;
+    } else {
+	/* insert */
+	e = malloc(sizeof(*e));
+	if (e == NULL)
+	    return ENOMEM;
+	ret = gsserrmap__copy_key (&e->key, key);
+	if (ret) {
+	    free(e);
+	    return ret;
+	}
+	e->value = new_value;
+	e->next = head->first;
+	head->first = e;
+    }
+    return 0;
+}
--- a/usr/src/lib/gss_mechs/mech_krb5/mech/export_name.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/gss_mechs/mech_krb5/mech/export_name.c	Mon Aug 16 17:01:32 2010 -0700
@@ -1,5 +1,6 @@
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
+/*
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
+ */
 /*
  * lib/gssapi/krb5/export_name.c
  *
@@ -52,16 +53,28 @@
 	exported_name->value = NULL;
 	
 	if (! kg_validate_name(input_name)) {
-		if (minor_status)
-			*minor_status = (OM_uint32) G_VALIDATE_FAILED;
-		krb5_free_context(context);
-		return(GSS_S_CALL_BAD_STRUCTURE|GSS_S_BAD_NAME);
+	    /* Solaris Kerberos: spruce-up the err msg */
+	    krb5_principal princ = (krb5_principal) input_name;
+	    char *s_name = NULL;
+	    int kret = krb5_unparse_name(context, princ, &s_name);
+	    if (minor_status)
+	        *minor_status = (OM_uint32) G_VALIDATE_FAILED;
+	    if (minor_status && kret == 0) {
+	        krb5_set_error_message(context, *minor_status,
+  "Input name principal '%s' is invalid (kg_validate_name()) for export_name",
+				    s_name);
+		save_error_info(*minor_status, context);
+		krb5_free_unparsed_name(context, s_name);
+	    }
+	    krb5_free_context(context);
+	    return(GSS_S_CALL_BAD_STRUCTURE|GSS_S_BAD_NAME);
 	}
 
 	if ((code = krb5_unparse_name(context, (krb5_principal) input_name, 
 				      &str))) {
 		if (minor_status)
 			*minor_status = code;
+		save_error_info((OM_uint32)code, context);
 		krb5_free_context(context);
 		return(GSS_S_FAILURE);
 	}
--- a/usr/src/lib/gss_mechs/mech_krb5/mech/export_sec_context.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/gss_mechs/mech_krb5/mech/export_sec_context.c	Mon Aug 16 17:01:32 2010 -0700
@@ -92,6 +92,9 @@
     return (GSS_S_COMPLETE);
 
 error_out:
+    if (retval != GSS_S_COMPLETE)
+        if (kret != 0 && context != 0)
+	    save_error_info((OM_uint32)kret, context);
     if (obuffer && bufsize) {
 	    memset(obuffer, 0, bufsize);
 	    xfree(obuffer);
--- a/usr/src/lib/gss_mechs/mech_krb5/mech/gss_libinit.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/gss_mechs/mech_krb5/mech/gss_libinit.c	Mon Aug 16 17:01:32 2010 -0700
@@ -1,9 +1,7 @@
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
-
 #include <assert.h>
 
 #include "gssapi_err_generic.h"
@@ -14,7 +12,6 @@
 #include "k5-platform.h"
 
 #include "mglueP.h"
-
 /*
  * Initialize the GSSAPI library.
  */
@@ -48,6 +45,10 @@
     err = k5_key_register(K5_KEY_GSS_KRB5_CCACHE_NAME, free);
     if (err)
 	return err;
+    err = k5_key_register(K5_KEY_GSS_KRB5_ERROR_MESSAGE,
+                          krb5_gss_delete_error_info);
+    if (err)
+	return err;
 #ifndef _WIN32
     err = k5_mutex_finish_init(&kg_kdc_flag_mutex);
     if (err)
--- a/usr/src/lib/gss_mechs/mech_krb5/mech/import_name.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/gss_mechs/mech_krb5/mech/import_name.c	Mon Aug 16 17:01:32 2010 -0700
@@ -1,5 +1,6 @@
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
+/*
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
+ */
 /*
  * Copyright 1993 by OpenVision Technologies, Inc.
  * 
@@ -38,6 +39,7 @@
 #else
 #include <strings.h>
 #endif
+#include <locale.h>
 
 /*
  * errors:
@@ -103,18 +105,24 @@
       xfree(tmp);
    } else if ((input_name_type != GSS_C_NULL_OID) &&
 	      (g_OID_equal(input_name_type, gss_nt_krb5_principal))) {
-      krb5_principal input;
+       krb5_principal input;
 
-      if (input_name_buffer->length != sizeof(krb5_principal)) {
-	 *minor_status = (OM_uint32) G_WRONG_SIZE;
-	 krb5_free_context(context);
-	 return(GSS_S_BAD_NAME);
+       if (input_name_buffer->length != sizeof(krb5_principal)) {
+	   *minor_status = (OM_uint32) G_WRONG_SIZE;
+	   /* Solaris Kerberos: spruce-up the err msg */
+	   krb5_set_error_message(context, *minor_status,
+				dgettext(TEXT_DOMAIN,
+					"The size of the specified principal is wrong"));
+	   save_error_info(*minor_status, context);
+	   krb5_free_context(context);
+	   return(GSS_S_BAD_NAME);
       }
 
       input = *((krb5_principal *) input_name_buffer->value);
 
       if ((code = krb5_copy_principal(context, input, &princ))) {
 	 *minor_status = code;
+	 save_error_info(*minor_status, context);
 	 krb5_free_context(context);
 	 return(GSS_S_FAILURE);
       }
@@ -200,11 +208,18 @@
 	 code = krb5_parse_name(context, (char *) stringrep, &princ);
       else {
       fail_name:
-	 xfree(tmp);
-	 if (tmp2)
+	  xfree(tmp);
+	  if (tmp2)
 		 xfree(tmp2);
-	 krb5_free_context(context);
-	 return(GSS_S_BAD_NAME);
+
+	  /* Solaris Kerberos: spruce-up (not much, sigh) the err msg */
+	  krb5_set_error_message(context, *minor_status,
+				dgettext(TEXT_DOMAIN,
+					"Failed to convert the specified principal to GSS-API internal format"));
+	  save_error_info(*minor_status, context);
+
+	  krb5_free_context(context);
+	  return(GSS_S_BAD_NAME);
       }
       
       if (tmp2)
@@ -216,9 +231,17 @@
       contains the return status */
 
    if (code) {
-      *minor_status = (OM_uint32) code;
-      krb5_free_context(context);
-      return(GSS_S_BAD_NAME);
+       /* Solaris Kerberos: spruce-up the err msg */
+       *minor_status = (OM_uint32) code;
+       /* krb5_sname_to_principal() sets specific err msg for bad hostname. */
+       if (*minor_status != (OM_uint32)KRB5_ERR_BAD_HOSTNAME)
+	    krb5_set_error_message(context, *minor_status,
+				dgettext(TEXT_DOMAIN,
+				  "Failed to convert the specified principal to GSS-API internal format: %s"),
+				error_message(code));
+       save_error_info(*minor_status, context);
+       krb5_free_context(context);
+       return(GSS_S_BAD_NAME);
    }
 
    /* save the name in the validation database */
--- a/usr/src/lib/gss_mechs/mech_krb5/mech/init_sec_context.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/gss_mechs/mech_krb5/mech/init_sec_context.c	Mon Aug 16 17:01:32 2010 -0700
@@ -1,9 +1,6 @@
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
-
-
 /*
  * Copyright 2000,2002, 2003 by the Massachusetts Institute of Technology.
  * All Rights Reserved.
@@ -724,11 +721,28 @@
 
    if (! krb5_principal_compare(context, ctx->there, 
 				(krb5_principal) target_name)) {
-      (void)krb5_gss_delete_sec_context(minor_status, 
+       /* Solaris Kerberos: spruce-up the err msg */
+       krb5_principal tname = (krb5_principal) target_name;
+       char *s_name = NULL, *s_princ= NULL;
+       int kret = krb5_unparse_name(context, tname, &s_name);
+       int kret1 = krb5_unparse_name(context, ctx->there, &s_princ);
+       code = KRB5_PRINC_NOMATCH;
+       if (kret == 0 && kret1 == 0) {
+	   krb5_set_error_message(context, code,
+				dgettext(TEXT_DOMAIN,
+					"Target name principal '%s' does not match '%s'"),
+				s_name, s_princ);
+	   save_error_info(code, context);
+       }
+       if (s_name)
+	   krb5_free_unparsed_name(context, s_name);
+       if (s_princ)
+	   krb5_free_unparsed_name(context, s_princ);
+
+       (void)krb5_gss_delete_sec_context(minor_status, 
 					context_handle, NULL);
-      code = 0;
-      major_status = GSS_S_BAD_NAME;
-      goto fail;
+       major_status = GSS_S_BAD_NAME;
+       goto fail;
    }
 
    /* verify the token and leave the AP_REP message in ap_rep */
@@ -875,8 +889,11 @@
 	   *minor_status = kerr;
 	   return GSS_S_FAILURE;
        }
-       if (GSS_ERROR(kg_sync_ccache_name(context, minor_status)))
+       if (GSS_ERROR(kg_sync_ccache_name(context, minor_status))) {
+	   save_error_info(*minor_status, context);
+	   krb5_free_context(context);
 	   return GSS_S_FAILURE;
+       }
    } else {
        context = ((krb5_gss_ctx_id_rec *)*context_handle)->k5_context;
    }
@@ -892,10 +909,23 @@
    /* verify that the target_name is valid and usable */
 
    if (! kg_validate_name(target_name)) {
-      *minor_status = (OM_uint32) G_VALIDATE_FAILED;
-      if (*context_handle == GSS_C_NO_CONTEXT)
-	  krb5_free_context(context);
-      return(GSS_S_CALL_BAD_STRUCTURE|GSS_S_BAD_NAME);
+       /* Solaris Kerberos: spruce-up the err msg */
+       krb5_principal princ = (krb5_principal) target_name;
+       char *s_name = NULL;
+       int kret = krb5_unparse_name(context, princ, &s_name);
+       *minor_status = (OM_uint32) G_VALIDATE_FAILED;
+       if (kret == 0) {
+	   krb5_set_error_message(context, *minor_status,
+				dgettext(TEXT_DOMAIN,
+					"Target name principal '%s' is invalid"),
+				s_name);
+	   krb5_free_unparsed_name(context, s_name);
+	   save_error_info(*minor_status, context);
+	}
+
+        if (*context_handle == GSS_C_NO_CONTEXT)
+	    krb5_free_context(context);
+        return(GSS_S_CALL_BAD_STRUCTURE|GSS_S_BAD_NAME);
    }
 
    /* verify the credential, or use the default */
@@ -909,13 +939,15 @@
       major_status = get_default_cred(minor_status, context,
 				    (gss_cred_id_t *)&cred);
       if (major_status && GSS_ERROR(major_status)) {
-	 if (*context_handle == GSS_C_NO_CONTEXT)
-	    krb5_free_context(context);
+	  save_error_info(*minor_status, context);
+	  if (*context_handle == GSS_C_NO_CONTEXT)
+	      krb5_free_context(context);
 	 return(major_status);
       }
    } else {
       major_status = krb5_gss_validate_cred(minor_status, claimant_cred_handle);
       if (GSS_ERROR(major_status)) {
+          save_error_info(*minor_status, context);
 	  if (*context_handle == GSS_C_NO_CONTEXT)
 	      krb5_free_context(context);
 	  return(major_status);
@@ -975,9 +1007,10 @@
 				    output_token, ret_flags, time_rec,
 				    context, default_mech);
       k5_mutex_unlock(&cred->lock);
-      if (*context_handle == GSS_C_NO_CONTEXT)
+      if (*context_handle == GSS_C_NO_CONTEXT) {
+          save_error_info (*minor_status, context);
 	  krb5_free_context(context);
-      else
+      } else
 	  ((krb5_gss_ctx_id_rec *) *context_handle)->k5_context = context;
    } else {
       /* mutual_auth doesn't care about the credentials */
@@ -1409,11 +1442,13 @@
 		} else {
 			/* Try to set a useful error message */
 			char *princ = NULL;
-			krb5_unparse_name(context, me, &princ);
+			krb5_error_code ret;
+			ret = krb5_unparse_name(context, me, &princ);
 
 			krb5_set_error_message(context, code,
-			    gettext("Failed to find realm for %s in keytab"),
-			    princ ? princ : "<unknown>");
+					    dgettext(TEXT_DOMAIN,
+						    "Failed to find realm for %s in keytab"),
+					    ret == 0 ? princ : "unknown");
 			if (princ)
 				krb5_free_unparsed_name(context, princ);
 		}
--- a/usr/src/lib/gss_mechs/mech_krb5/mech/krb5_gss_glue.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/gss_mechs/mech_krb5/mech/krb5_gss_glue.c	Mon Aug 16 17:01:32 2010 -0700
@@ -1,6 +1,5 @@
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 /*
  * Copyright 1993 by OpenVision Technologies, Inc.
@@ -1391,6 +1390,7 @@
      */
     if ((data_set == GSS_C_NO_BUFFER_SET) || (data_set->count == 0)) {
 	    gss_release_buffer_set(minor_status, &data_set);
+	    *minor_status = EINVAL;
 	    return GSS_S_FAILURE;
     }
 
--- a/usr/src/lib/gss_mechs/mech_krb5/mech/rel_cred.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/gss_mechs/mech_krb5/mech/rel_cred.c	Mon Aug 16 17:01:32 2010 -0700
@@ -1,5 +1,6 @@
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
+/*
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
+ */
 /*
  * Copyright 1993 by OpenVision Technologies, Inc.
  * 
@@ -90,7 +91,6 @@
        free(cred->req_enctypes);
 
    xfree(cred);
-   krb5_free_context(context);
 
    *cred_handle = NULL;
 
@@ -102,5 +102,8 @@
    if (code3)
       *minor_status = code3;
 
+   if (*minor_status)
+     save_error_info(*minor_status, context);
+   krb5_free_context(context);
    return(*minor_status?GSS_S_FAILURE:GSS_S_COMPLETE);
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/gss_mechs/mech_krb5/mech/util_errmap.c	Mon Aug 16 17:01:32 2010 -0700
@@ -0,0 +1,286 @@
+/*   Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. */
+/* -*- mode: c; indent-tabs-mode: nil -*- */
+/*
+ * Copyright 2007, 2008 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 "gssapiP_generic.h"
+#include "mechglueP.h"
+#include <string.h>
+#include <stdio.h>
+#ifndef _WIN32
+#include <unistd.h>
+#endif
+
+/* Solaris Kerberos */
+#define inline
+#ifdef DEBUG
+#undef DEBUG
+#endif
+
+/* The mapping table is 0-based, but let's export codes that are
+   1-based, keeping 0 for errors or unknown errors.
+
+   The elements in the mapping table currently have separate copies of
+   each OID stored.  This is a bit wasteful, but we are assuming the
+   table isn't likely to grow very large.  */
+
+struct mecherror {
+    gss_OID_desc mech;
+    OM_uint32 code;
+};
+
+static inline int
+cmp_OM_uint32(OM_uint32 m1, OM_uint32 m2)
+{
+    if (m1 < m2)
+        return -1;
+    else if (m1 > m2)
+        return 1;
+    else
+        return 0;
+}
+
+static inline int
+mecherror_cmp(struct mecherror m1, struct mecherror m2)
+{
+    if (m1.code < m2.code)
+        return -1;
+    if (m1.code > m2.code)
+        return 1;
+    if (m1.mech.length < m2.mech.length)
+        return -1;
+    if (m1.mech.length > m2.mech.length)
+        return 1;
+    if (m1.mech.length == 0)
+        return 0;
+    return memcmp(m1.mech.elements, m2.mech.elements, m1.mech.length);
+}
+
+static void
+print_OM_uint32 (OM_uint32 value, FILE *f)
+{
+    fprintf(f, "%lu", (unsigned long) value);
+}
+
+static inline int
+mecherror_copy(struct mecherror *dest, struct mecherror src)
+{
+    *dest = src;
+    dest->mech.elements = malloc(src.mech.length);
+    if (dest->mech.elements == NULL) {
+        if (src.mech.length)
+            return ENOMEM;
+        else
+            return 0;
+    }
+    memcpy(dest->mech.elements, src.mech.elements, src.mech.length);
+    return 0;
+}
+
+static void
+mecherror_print(struct mecherror value, FILE *f)
+{
+    OM_uint32 minor;
+    gss_buffer_desc str;
+    static const struct {
+        const char *oidstr, *name;
+    } mechnames[] = {
+        { "{ 1 2 840 113554 1 2 2 }", "krb5-new" },
+        { "{ 1 3 5 1 5 2 }", "krb5-old" },
+        { "{ 1 2 840 48018 1 2 2 }", "krb5-microsoft" },
+        { "{ 1 3 6 1 5 5 2 }", "spnego" },
+    };
+    unsigned int i;
+
+    fprintf(f, "%lu@", (unsigned long) value.code);
+
+    if (value.mech.length == 0) {
+        fprintf(f, "(com_err)");
+        return;
+    }
+    fprintf(f, "%p=", value.mech.elements);
+    if (generic_gss_oid_to_str(&minor, &value.mech, &str)) {
+        fprintf(f, "(error in conversion)");
+        return;
+    }
+    /* Note: generic_gss_oid_to_str returns a null-terminated string.  */
+    for (i = 0; i < sizeof(mechnames)/sizeof(mechnames[0]); i++) {
+        if (!strcmp(str.value, mechnames[i].oidstr) && mechnames[i].name != 0) {
+            fprintf(f, "%s", mechnames[i].name);
+            break;
+        }
+    }
+    if (i == sizeof(mechnames)/sizeof(mechnames[0]))
+        fprintf(f, "%s", (char *) str.value);
+    generic_gss_release_buffer(&minor, &str);
+}
+
+#include "errmap.h"
+#include "krb5.h"               /* for KRB5KRB_AP_WRONG_PRINC */
+
+static mecherrmap m;
+static k5_mutex_t mutex = K5_MUTEX_PARTIAL_INITIALIZER;
+static OM_uint32 next_fake = 100000;
+
+int gssint_mecherrmap_init(void)
+{
+    int err;
+
+    err = mecherrmap_init(&m);
+    if (err)
+        return err;
+    err = k5_mutex_finish_init(&mutex);
+    if (err) {
+        mecherrmap_destroy(&m);
+        return err;
+    }
+
+    return 0;
+}
+
+/* Currently the enumeration template doesn't handle freeing
+   element storage when destroying the collection.  */
+static int free_one(OM_uint32 i, struct mecherror value, void *p)
+{
+    if (value.mech.length && value.mech.elements)
+        free(value.mech.elements);
+    return 0;
+}
+
+void gssint_mecherrmap_destroy(void)
+{
+    mecherrmap_foreach(&m, free_one, NULL);
+    mecherrmap_destroy(&m);
+    k5_mutex_destroy(&mutex);
+}
+
+OM_uint32 gssint_mecherrmap_map(OM_uint32 minor, const gss_OID_desc * oid)
+{
+    const struct mecherror *mep;
+    struct mecherror me, me_copy;
+    const OM_uint32 *p;
+    int err;
+    OM_uint32 new_status;
+
+#ifdef DEBUG
+    FILE *f;
+    f = fopen("/dev/pts/9", "w+");
+    if (f == NULL)
+        f = stderr;
+#endif
+
+    me.code = minor;
+    me.mech = *oid;
+    err = k5_mutex_lock(&mutex);
+    if (err) {
+#ifdef DEBUG
+        if (f != stderr) fclose(f);
+#endif
+        return 0;
+    }
+
+    /* Is this status+oid already mapped?  */
+    p = mecherrmap_findright(&m, me);
+    if (p != NULL) {
+        k5_mutex_unlock(&mutex);
+#ifdef DEBUG
+        fprintf(f, "%s: found ", __func__);
+        mecherror_print(me, f);
+        fprintf(f, " in map as %lu\n", (unsigned long) *p);
+        if (f != stderr) fclose(f);
+#endif
+        return *p;
+    }
+    /* Is this status code already mapped to something else
+       mech-specific?  */
+    mep = mecherrmap_findleft(&m, minor);
+    if (mep == NULL) {
+        /* Map it to itself plus this mech-oid.  */
+        new_status = minor;
+    } else {
+        /* Already assigned.  Pick a fake new value and map it.  */
+        /* There's a theoretical infinite loop risk here, if we fill
+           in 2**32 values.  Also, returning 0 has a special
+           meaning.  */
+        do {
+            next_fake++;
+            new_status = next_fake;
+            if (new_status == 0)
+                /* ??? */;
+        } while (mecherrmap_findleft(&m, new_status) != NULL);
+    }
+    err = mecherror_copy(&me_copy, me);
+    if (err) {
+        k5_mutex_unlock(&mutex);
+        return err;
+    }
+    err = mecherrmap_add(&m, new_status, me_copy);
+    k5_mutex_unlock(&mutex);
+    if (err) {
+        if (me_copy.mech.length)
+            free(me_copy.mech.elements);
+    }
+#ifdef DEBUG
+    fprintf(f, "%s: mapping ", __func__);
+    mecherror_print(me, f);
+    fprintf(f, " to %lu: err=%d\nnew map: ", (unsigned long) new_status, err);
+    mecherrmap_printmap(&m, f);
+    fprintf(f, "\n");
+    if (f != stderr) fclose(f);
+#endif
+
+    if (err)
+        return 0;
+    else
+        return new_status;
+}
+
+static gss_OID_desc no_oid = { 0, 0 };
+OM_uint32 gssint_mecherrmap_map_errcode(OM_uint32 errcode)
+{
+    return gssint_mecherrmap_map(errcode, &no_oid);
+}
+
+int gssint_mecherrmap_get(OM_uint32 minor, gss_OID mech_oid,
+                          OM_uint32 *mech_minor)
+{
+    const struct mecherror *p;
+    int err;
+
+    if (minor == 0) {
+        return EINVAL;
+    }
+    err = k5_mutex_lock(&mutex);
+    if (err)
+        return err;
+    p = mecherrmap_findleft(&m, minor);
+    k5_mutex_unlock(&mutex);
+    if (!p) {
+        return EINVAL;
+    }
+    *mech_oid = p->mech;
+    *mech_minor = p->code;
+    return 0;
+}
--- a/usr/src/lib/gss_mechs/mech_spnego/Makefile.com	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/gss_mechs/mech_spnego/Makefile.com	Mon Aug 16 17:01:32 2010 -0700
@@ -19,8 +19,7 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
 #
 
 
@@ -32,7 +31,7 @@
 
 LIBRARY = 	mech_spnego.a
 VERS = 		.1
-OBJECTS = 	spnego_mech.o
+OBJECTS = 	spnego_mech.o spnego_disp_status.o spnego_kerrs.o
 
 # include library definitions
 include ../../../Makefile.lib
@@ -47,7 +46,7 @@
 		$(CLOSED)/lib/gss_mechs/mech_spnego/mapfile-vers-export
 MAPFILES =	../mapfile-vers $(MAPFILE_EXPORT)
 
-CPPFLAGS += -I$(SRC)/uts/common/gssapi/include $(DEBUG) -I$(SRC)/lib/gss_mechs/mech_krb5/include -I$(SRC)/uts/common/gssapi/mechs/krb5/include
+CPPFLAGS += -I$(SRC)/uts/common/gssapi/include $(DEBUG) -I$(SRC)/lib/gss_mechs/mech_krb5/include -I$(SRC)/uts/common/gssapi/mechs/krb5/include -I$(SRC)/lib/gss_mechs/mech_krb5/mech
 
 MAKEFILE_EXPORT = $(CLOSED)/lib/gss_mechs/mech_spnego/Makefile.export
 $(EXPORT_RELEASE_BUILD)include $(MAKEFILE_EXPORT)
--- a/usr/src/lib/gss_mechs/mech_spnego/mech/gssapiP_spnego.h	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/gss_mechs/mech_spnego/mech/gssapiP_spnego.h	Mon Aug 16 17:01:32 2010 -0700
@@ -1,13 +1,9 @@
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
-
 #ifndef	_GSSAPIP_SPNEGO_H_
 #define	_GSSAPIP_SPNEGO_H_
 
-/* #pragma ident	"@(#)gssapiP_spnego.h	1.3	03/09/18 SMI" */
-
 #ifdef	__cplusplus
 extern "C" {
 #endif
@@ -100,6 +96,7 @@
 	OM_uint32 ctx_flags;
 	gss_name_t internal_name;
 	gss_OID actual_mech;
+        struct errinfo err;
 } spnego_gss_ctx_id_rec, *spnego_gss_ctx_id_t;
 
 /*
@@ -112,10 +109,6 @@
 extern const gss_OID_desc * const gss_mech_spnego;
 extern const gss_OID_set_desc * const gss_mech_set_spnego;
 
-/* SUNW17PACresync */
-#define	TWRITE_STR(ptr, str, len) \
-	memcpy((ptr), (char *)(str), (len)); \
-	(ptr) += (len);
 
 #ifdef DEBUG
 #define	dsyslog(a) syslog(LOG_DEBUG, a)
@@ -282,6 +275,16 @@
 	gss_buffer_t		/* status_string */
 );
 
+OM_uint32 spnego_gss_display_status2
+(
+	OM_uint32 *,		/* minor_status */
+	OM_uint32,		/* status_value */
+	int,			/* status_type */
+	gss_OID,		/* mech_type */
+	OM_uint32 *,		/* message_context */
+	gss_buffer_t		/* status_string */
+);
+
 OM_uint32 glue_spnego_gss_display_status
 (
 	void *,
@@ -507,13 +510,6 @@
 );
 
 
-#ifdef _GSS_STATIC_LINK
-int gss_spnegoint_lib_init(void);
-void gss_spnegoint_lib_fini(void);
-#else
-gss_mechanism KRB5_CALLCONV gss_mech_initialize(void);
-#endif /* _GSS_STATIC_LINK */
-
 #if 0 /* SUNW17PACresync - will be needed for full MIT 1.7 resync */
 OM_uint32 spnego_gss_wrap_aead
 (
@@ -579,6 +575,26 @@
 );
 #endif /* 0 */
 
+/*
+ * Solaris SPNEGO
+ * Cloned the krb5_*_error_message and krb5_gss_*_error_info APIs
+ * to give similar functionality to SPNEGO mech.
+ * See new files in this dir:
+ *     spnego_disp_status.c
+ *     spnego_kerrs.c
+ *     error_map.h
+ */
+typedef int spnego_error_code;
+void spnego_set_error_message (spnego_gss_ctx_id_t, spnego_error_code, const char *, ...);
+const char * spnego_get_error_message (spnego_gss_ctx_id_t, spnego_error_code);
+void spnego_free_error_message (spnego_gss_ctx_id_t, const char *);
+void spnego_clear_error_message (spnego_gss_ctx_id_t);
+
+void spnego_gss_save_error_info(OM_uint32 minor_code, spnego_gss_ctx_id_t ctx);
+char *spnego_gss_get_error_message(OM_uint32 minor_code);
+void spnego_gss_delete_error_info(void *p);
+
+OM_uint32 krb5_gss_display_status2();
 #ifdef	__cplusplus
 }
 #endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/gss_mechs/mech_spnego/mech/spnego_disp_status.c	Mon Aug 16 17:01:32 2010 -0700
@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+/*
+ * Copyright 1993 by OpenVision Technologies, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear in
+ * supporting documentation, and that the name of OpenVision not be used
+ * in advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission. OpenVision makes no
+ * representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
+ * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include        <sys/param.h>
+#include        <unistd.h>
+#include        <assert.h>
+#include        <stdio.h>
+#include        <stdlib.h>
+#include        <string.h>
+#include        <k5-int.h>
+#include        <krb5.h>
+#include        <mglueP.h>
+#include        "gssapiP_spnego.h"
+#include        "gssapiP_generic.h"
+#include        <gssapi_err_generic.h>
+
+/* X internationalization!! */
+
+static inline int
+compare_OM_uint32 (OM_uint32 a, OM_uint32 b)
+{
+    if (a < b)
+        return -1;
+    else if (a == b)
+        return 0;
+    else
+        return 1;
+}
+static inline void
+free_string (char *s)
+{
+    free(s);
+}
+#include "error_map.h"
+#include <stdio.h>
+
+#define get_error_message spnego_gss_get_error_message
+char *get_error_message(OM_uint32 minor_code)
+{
+    gsserrmap *p = k5_getspecific(K5_KEY_GSS_SPNEGO_ERROR_MESSAGE);
+    char *msg = NULL;
+
+#ifdef DEBUG
+    fprintf(stderr, "%s(%lu, p=%p)", __func__, (unsigned long) minor_code,
+            (void *) p);
+#endif
+    if (p) {
+        char **v = gsserrmap_find(p, minor_code);
+        if (v) {
+            msg = *v;
+#ifdef DEBUG
+            fprintf(stderr, " FOUND!");
+#endif
+        }
+    }
+    if (msg == 0)
+        msg = (char *)error_message(minor_code);
+#ifdef DEBUG
+    fprintf(stderr, " -> %p/%s\n", (void *) msg, msg);
+#endif
+
+    return msg;
+}
+
+static int save_error_string_nocopy(OM_uint32 minor_code, char *msg)
+{
+    gsserrmap *p;
+    int ret;
+
+#ifdef DEBUG
+    fprintf(stderr, "%s(%lu, %s)", __func__, (unsigned long) minor_code, msg);
+#endif
+    p = k5_getspecific(K5_KEY_GSS_SPNEGO_ERROR_MESSAGE);
+    if (!p) {
+        p = malloc(sizeof(*p));
+        if (p == NULL) {
+            ret = 1;
+            goto fail;
+        }
+        if (gsserrmap_init(p) != 0) {
+            free(p);
+            p = NULL;
+            ret = 1;
+            goto fail;
+        }
+        if (k5_setspecific(K5_KEY_GSS_SPNEGO_ERROR_MESSAGE, p) != 0) {
+            gsserrmap_destroy(p);
+            free(p);
+            p = NULL;
+            ret = 1;
+            goto fail;
+        }
+    }
+    ret = gsserrmap_replace_or_insert(p, minor_code, msg);
+    /* Solaris SPNEGO */
+    if (ret) {
+            gsserrmap_destroy(p);
+            free(p);
+            p = NULL;
+    }
+
+fail:
+#ifdef DEBUG
+    fprintf(stderr, " p=%p %s\n", (void *)p, ret ? "FAIL" : "SUCCESS");
+#endif
+    return ret;
+}
+void save_error_string(OM_uint32 minor_code, char *msg)
+{
+    char *s = strdup(msg);
+    if (s) {
+        if (save_error_string_nocopy(minor_code, s) != 0)
+            free(s);
+    }
+}
+void save_error_message(OM_uint32 minor_code, const char *format, ...)
+{
+    char *s;
+    int n;
+    va_list ap;
+
+    va_start(ap, format);
+    n = vasprintf(&s, format, ap);
+    va_end(ap);
+    if (n >= 0) {
+        if (save_error_string_nocopy(minor_code, s) != 0)
+            free(s);
+    }
+}
+void spnego_gss_save_error_info(OM_uint32 minor_code, spnego_gss_ctx_id_t ctx)
+{
+    char *s;
+
+#ifdef DEBUG
+    fprintf(stderr, "%s(%lu, ctx=%p)\n", __func__,
+            (unsigned long) minor_code, (void *)ctx);
+#endif
+    s = (char *)spnego_get_error_message(ctx,  minor_code);
+#ifdef DEBUG
+    fprintf(stderr, "%s(%lu, ctx=%p) saving: %s\n", __func__,
+            (unsigned long) minor_code, (void *)ctx, s);
+#endif
+    save_error_string(minor_code, s);
+    /* The get_error_message call above resets the error message in
+       ctx.  Put it back, in case we make this call again *sigh*.  */
+    spnego_set_error_message(ctx, minor_code, "%s", s);
+    spnego_free_error_message(ctx, s);
+}
+void spnego_gss_delete_error_info(void *p)
+{
+    gsserrmap_destroy(p);
+}
+
+OM_uint32
+spnego_gss_display_status2(minor_status, status_value, status_type,
+                        mech_type, message_context, status_string)
+    OM_uint32 *minor_status;
+    OM_uint32 status_value;
+    int status_type;
+    gss_OID mech_type;
+    OM_uint32 *message_context;
+    gss_buffer_t status_string;
+{
+    status_string->length = 0;
+    status_string->value = NULL;
+
+    if ((mech_type != GSS_C_NULL_OID) &&
+        !g_OID_equal(gss_mech_spnego, mech_type)) {
+        *minor_status = 0;
+        return(GSS_S_BAD_MECH);
+    }
+
+    if (status_type == GSS_C_GSS_CODE) {
+        return(g_display_major_status(minor_status, status_value,
+                                      message_context, status_string));
+    } else if (status_type == GSS_C_MECH_CODE) {
+	/*
+	 * Solaris SPNEGO
+	 * This init call appears to be not needed as
+	 * gss_spnegoint_lib_init() is called on dl open.
+	 */
+#if 0
+        (void) gss_spnegoint_initialize_library();
+#endif
+
+        if (*message_context) {
+            *minor_status = (OM_uint32) G_BAD_MSG_CTX;
+            return(GSS_S_FAILURE);
+        }
+
+        /* If this fails, there's not much we can do...  */
+        if (g_make_string_buffer(spnego_gss_get_error_message(status_value),
+                                 status_string) != 0) {
+            *minor_status = ENOMEM;
+	    return(GSS_S_FAILURE);
+        } else
+            *minor_status = 0;
+        return(0);
+    } else {
+        *minor_status = 0;
+        return(GSS_S_BAD_STATUS);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/gss_mechs/mech_spnego/mech/spnego_kerrs.c	Mon Aug 16 17:01:32 2010 -0700
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+/*
+ * lib/krb5/krb/kerrs.c
+ *
+ * Copyright 2006 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.
+ *
+ * error-message functions
+ */
+#include        <sys/param.h>
+#include        <unistd.h>
+#include        <assert.h>
+#include        <stdio.h>
+#include        <stdlib.h>
+#include        <string.h>
+#include        <k5-int.h>
+#include        <krb5.h>
+#include        <mglueP.h>
+#include        "gssapiP_spnego.h"
+#include        "gssapiP_generic.h"
+#include        <gssapi_err_generic.h>
+
+#ifdef DEBUG
+static int error_message_debug = 0;
+#ifndef ERROR_MESSAGE_DEBUG
+#define ERROR_MESSAGE_DEBUG() (error_message_debug != 0)
+#endif
+#endif
+
+void
+spnego_set_error_message (spnego_gss_ctx_id_t ctx, spnego_error_code code,
+			const char *fmt, ...)
+{
+    va_list args;
+    if (ctx == NULL)
+	return;
+    va_start (args, fmt);
+#ifdef DEBUG
+    if (ERROR_MESSAGE_DEBUG())
+	fprintf(stderr,
+		"spnego_set_error_message(ctx=%p/err=%p, code=%ld, ...)\n",
+		ctx, &ctx->err, (long) code);
+#endif
+    krb5int_vset_error (&ctx->err, code, fmt, args);
+#ifdef DEBUG
+    if (ERROR_MESSAGE_DEBUG())
+	fprintf(stderr, "->%s\n", ctx->err.msg);
+#endif
+    va_end (args);
+}
+
+void
+spnego_vset_error_message (spnego_gss_ctx_id_t ctx, spnego_error_code code,
+			 const char *fmt, va_list args)
+{
+#ifdef DEBUG
+    if (ERROR_MESSAGE_DEBUG())
+	fprintf(stderr, "spnego_vset_error_message(ctx=%p, code=%ld, ...)\n",
+		ctx, (long) code);
+#endif
+    if (ctx == NULL)
+	return;
+    krb5int_vset_error (&ctx->err, code, fmt, args);
+#ifdef DEBUG
+    if (ERROR_MESSAGE_DEBUG())
+	fprintf(stderr, "->%s\n", ctx->err.msg);
+#endif
+}
+
+const char *
+spnego_get_error_message (spnego_gss_ctx_id_t ctx, spnego_error_code code)
+{
+#ifdef DEBUG
+    if (ERROR_MESSAGE_DEBUG())
+	fprintf(stderr, "spnego_get_error_message(%p, %ld)\n", ctx, (long) code);
+#endif
+    if (ctx == NULL)
+	return error_message(code);
+    return krb5int_get_error (&ctx->err, code);
+}
+
+void
+spnego_free_error_message (spnego_gss_ctx_id_t ctx, const char *msg)
+{
+#ifdef DEBUG
+    if (ERROR_MESSAGE_DEBUG())
+	fprintf(stderr, "spnego_free_error_message(%p, %p)\n", ctx, msg);
+#endif
+    if (ctx == NULL)
+	return;
+    krb5int_free_error (&ctx->err, msg);
+}
+
+void
+spnego_clear_error_message (spnego_gss_ctx_id_t ctx)
+{
+#ifdef DEBUG
+    if (ERROR_MESSAGE_DEBUG())
+	fprintf(stderr, "spnego_clear_error_message(%p)\n", ctx);
+#endif
+    if (ctx == NULL)
+	return;
+    krb5int_clear_error (&ctx->err);
+}
--- a/usr/src/lib/gss_mechs/mech_spnego/mech/spnego_mech.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/gss_mechs/mech_spnego/mech/spnego_mech.c	Mon Aug 16 17:01:32 2010 -0700
@@ -1,3 +1,6 @@
+/*
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
+ */
 /*
  * Copyright (C) 2006,2008 by the Massachusetts Institute of Technology.
  * All rights reserved.
@@ -24,9 +27,6 @@
  */
 
 /*
- * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
- *
  * A module that implements the spnego security mechanism.
  * It is used to negotiate the security mechanism between
  * peers using the GSS-API.
@@ -72,8 +72,9 @@
 #include	<krb5.h>
 #include	<mglueP.h>
 #include	"gssapiP_spnego.h"
+#include        "gssapiP_generic.h"
 #include	<gssapi_err_generic.h>
-
+#include	<locale.h>
 
 /*
  * SUNW17PACresync
@@ -217,6 +218,12 @@
 acc_ctx_hints(OM_uint32 *, gss_ctx_id_t *, gss_cred_id_t,
 	      gss_buffer_t *, OM_uint32 *, send_token_flag *);
 
+#ifdef _GSS_STATIC_LINK
+int gss_spnegoint_lib_init(void);
+void gss_spnegoint_lib_fini(void);
+#else
+gss_mechanism gss_mech_initialize(void);
+#endif /* _GSS_STATIC_LINK */
 
 /*
  * The Mech OID for SPNEGO:
@@ -286,7 +293,8 @@
 #ifdef _GSS_STATIC_LINK
 #include "mglueP.h"
 
-static int gss_spnegomechglue_init(void)
+static
+int gss_spnegomechglue_init(void)
 {
 	struct gss_mech_config mech_spnego;
 
@@ -298,25 +306,48 @@
 	return gssint_register_mechinfo(&mech_spnego);
 }
 #else
+/* Entry point for libgss */
 gss_mechanism KRB5_CALLCONV
 gss_mech_initialize(void)
 {
+	int err;
+
+	err = k5_key_register(K5_KEY_GSS_SPNEGO_ERROR_MESSAGE,
+		spnego_gss_delete_error_info);
+	if (err) {
+	    syslog(LOG_NOTICE,
+		"SPNEGO gss_mech_initialize: error message TSD key register fail");
+	    return (NULL);
+	}
+
 	return (&spnego_mechanism);
 }
 
 #if 0 /* SUNW17PACresync */
 MAKE_INIT_FUNCTION(gss_krb5int_lib_init);
 MAKE_FINI_FUNCTION(gss_krb5int_lib_fini);
- int gss_krb5int_lib_init(void)
+int gss_krb5int_lib_init(void)
 #endif
 
 #endif /* _GSS_STATIC_LINK */
 
-static int gss_spnegoint_lib_init(void)
+static
+int gss_spnegoint_lib_init(void)
 {
 #ifdef _GSS_STATIC_LINK
 	return gss_spnegomechglue_init();
 #else
+	int err;
+
+	err = k5_key_register(K5_KEY_GSS_SPNEGO_ERROR_MESSAGE,
+		spnego_gss_delete_error_info);
+	if (err) {
+	    syslog(LOG_NOTICE,
+		"SPNEGO gss_mech_initialize: error message TSD key register fail: err=%d",
+		err);
+	    return err;
+	}
+
 	return 0;
 #endif
 }
@@ -465,7 +496,8 @@
 	spnego_ctx->nego_done = 0;
 	spnego_ctx->internal_name = GSS_C_NO_NAME;
 	spnego_ctx->actual_mech = GSS_C_NO_OID;
-
+	spnego_ctx->err.msg = NULL;
+	spnego_ctx->err.scratch_buf[0] = 0;
 	check_spnego_options(spnego_ctx);
 
 	return (spnego_ctx);
@@ -679,6 +711,10 @@
 	}
 	if (acc_negState == REJECT) {
 		*minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
+		/* Solaris SPNEGO */
+		spnego_set_error_message(sc, *minor_status,
+					dgettext(TEXT_DOMAIN,
+						"SPNEGO failed to negotiate a mechanism: server rejected request"));
 		map_errcode(minor_status);
 		*tokflag = NO_TOKEN_SEND;
 		ret = GSS_S_FAILURE;
@@ -742,6 +778,10 @@
 	}
 	if (acc_negState == ACCEPT_DEFECTIVE_TOKEN) {
 		*minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
+		/* Solaris SPNEGO */
+		spnego_set_error_message(sc, *minor_status,
+					dgettext(TEXT_DOMAIN,
+						"SPNEGO failed to negotiate a mechanism: defective token"));
 		map_errcode(minor_status);
 		return GSS_S_DEFECTIVE_TOKEN;
 	}
@@ -1004,6 +1044,11 @@
 		}
 	}
 	spnego_ctx = (spnego_gss_ctx_id_t)*context_handle;
+
+	/* Solaris SPNEGO */
+	if (*minor_status == ERR_SPNEGO_NEGOTIATION_FAILED)
+		spnego_gss_save_error_info(*minor_status, spnego_ctx);
+
 	if (!spnego_ctx->mech_complete) {
 		ret = init_ctx_call_init(
 			minor_status, spnego_ctx,
@@ -1339,6 +1384,76 @@
 }
 
 /*
+ * Solaris SPNEGO
+ * mechoidset2str()
+ * Input an OID set of mechs and output a string like so:
+ *   '{ x y z } (mechname0), { a b c } (mechname1) ...'.
+ * On error return NULL.
+ * Caller needs to free returned string.
+ */
+static const char *mech_no_map = "Can't map OID to mechname via /etc/gss/mech";
+static const char *oid_no_map = "Can't map OID to string";
+static char *
+mechoidset2str(gss_OID_set mechset)
+{
+	int i, l;
+	char buf[256] = {0};
+	char *s = NULL;
+
+	if (!mechset)
+		return NULL;
+
+	for (i = 0; i < mechset->count; i++) {
+		OM_uint32 maj, min;
+		gss_buffer_desc oidstr;
+		gss_buffer_t oidstrp = &oidstr;
+		gss_OID mech_oid = &mechset->elements[i];
+		/* No need to free mech_name. */
+		const char *mech_name = __gss_oid_to_mech(mech_oid);
+
+		if (i > 0)
+			if (strlcat(buf, ", ", sizeof (buf)) >= sizeof (buf)) {
+				if (oidstrp->value)
+					gss_release_buffer(&min, oidstrp);
+				break;
+			}
+
+		/* Add '{ x y x ... }'. */
+		maj = gss_oid_to_str(&min, mech_oid, oidstrp);
+		if (strlcat(buf, maj ? oid_no_map : oidstrp->value,
+			    sizeof (buf)) >= sizeof (buf)) {
+			if (oidstrp->value)
+				gss_release_buffer(&min, oidstrp);
+			break;
+		}
+		if (oidstrp->value)
+			gss_release_buffer(&min, oidstrp);
+
+		/* Add '(mech name)'. */
+		if (strlcat(buf, " (", sizeof (buf)) >= sizeof (buf))
+			break;
+		if (strlcat(buf, mech_name ? mech_name : mech_no_map,
+			    sizeof (buf)) >= sizeof (buf))
+			break;
+		if (strlcat(buf, ") ", sizeof (buf)) >= sizeof (buf))
+			break;
+	}
+
+	/* Even if we have buf overflow, let's output what we got so far. */
+	if (mechset->count) {
+		l = strlen(buf);
+		if (l > 0) {
+			s = malloc(l + 1);
+			if (!s)
+				return NULL;
+			(void) strlcpy(s, buf, l);
+		}
+	}
+
+	return s ? s : NULL;
+}
+
+/*
  * Set negState to REJECT if the token is defective, else
  * ACCEPT_INCOMPLETE or REQUEST_MIC, depending on whether initiator's
  * preferred mechanism is supported.
@@ -1396,13 +1511,33 @@
 	 * the acceptor will support.
 	 */
 	mech_wanted = negotiate_mech_type(minor_status,
-					  supported_mechSet,
-					  mechTypes,
-					  negState);
+					supported_mechSet,
+					mechTypes,
+					negState);
 	if (*negState == REJECT) {
+		/* Solaris SPNEGO: Spruce-up error msg */
+		char *mechTypesStr = mechoidset2str(mechTypes);
+		spnego_gss_ctx_id_t tmpsc = create_spnego_ctx();
+		if (tmpsc && *minor_status == ERR_SPNEGO_NEGOTIATION_FAILED) {
+			spnego_set_error_message(tmpsc, *minor_status,
+						dgettext(TEXT_DOMAIN,
+							"SPNEGO failed to negotiate a mechanism: client requested mech set '%s'"),
+				mechTypesStr ? mechTypesStr : "<null>");
+		}
+		if (mechTypesStr)
+			free(mechTypesStr);
+
+		/*
+		 * We save error here cuz the tmp ctx goes away (very) soon.
+		 * So callers of acc_ctx_new() should NOT call it again.
+		 */
+		spnego_gss_save_error_info(*minor_status, tmpsc);
+		if (tmpsc)
+			release_spnego_ctx(&tmpsc);
 		ret = GSS_S_BAD_MECH;
 		goto cleanup;
 	}
+
 	sc = (spnego_gss_ctx_id_t)*ctx;
 	if (sc != NULL) {
 		gss_release_buffer(&tmpmin, &sc->DER_mechTypes);
@@ -1535,6 +1670,31 @@
 	mech = gssint_get_mechanism(mechoid);
 	if (mech == NULL || mech->gss_indicate_mechs == NULL) {
 		*minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
+		{
+			/*
+			 * Solaris SPNEGO
+			 * Spruce-up error msg.
+			 */
+			OM_uint32 maj, maj_sc, min;
+			gss_buffer_desc oidstr, oidstr_sc;
+			/* No need to free mnamestr. */
+			const char *mnamestr = __gss_oid_to_mech(
+				sc->internal_mech);
+			maj_sc = gss_oid_to_str(&min,
+						sc->internal_mech,
+						&oidstr_sc);
+			maj = gss_oid_to_str(&min, mechoid, &oidstr);
+			spnego_set_error_message(sc, *minor_status,
+						dgettext(TEXT_DOMAIN,
+							"SPNEGO failed to negotiate a mechanism: unsupported mech OID ('%s') in the token. Negotiated mech OID is '%s' (%s)"),
+					maj ? oid_no_map: oidstr.value,
+					maj_sc ? oid_no_map: oidstr_sc.value,
+					mnamestr ? mnamestr : mech_no_map);
+			if (!maj)
+			        (void) gss_release_buffer(&min, &oidstr);
+			if (!maj_sc)
+			        (void) gss_release_buffer(&min, &oidstr_sc);
+		}
 		map_errcode(minor_status);
 		*negState = REJECT;
 		*tokflag = ERROR_TOKEN_SEND;
@@ -1551,7 +1711,30 @@
 	if (ret != GSS_S_COMPLETE)
 		goto cleanup;
 	if (!present) {
-		*minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
+		{
+			/*
+			 * Solaris SPNEGO
+			 * Spruce-up error msg.
+			 */
+			OM_uint32 maj, min;
+			gss_buffer_desc oidstr;
+			char *mech_set_str = mechoidset2str(mech_set);
+			/* No need to free mnamestr. */
+			const char *mnamestr =
+				__gss_oid_to_mech(sc->internal_mech);
+			maj = gss_oid_to_str(&min, sc->internal_mech, &oidstr);
+			*minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
+			spnego_set_error_message(sc, *minor_status,
+						dgettext(TEXT_DOMAIN,
+							"SPNEGO failed to negotiate a mechanism: negotiated mech OID '%s' (%s) not found in mechset ('%s') of token mech"),
+				maj ? oid_no_map: oidstr.value,
+				mnamestr ? mnamestr : mech_no_map,
+				mech_set_str ? mech_set_str : "<null>");
+			if (!maj)
+			        (void) gss_release_buffer(&min, &oidstr);
+			if (mech_set_str)
+				free(mech_set_str);
+		}
 		map_errcode(minor_status);
 		*negState = REJECT;
 		*tokflag = ERROR_TOKEN_SEND;
@@ -1587,7 +1770,7 @@
 			return ret;
 		}
 		ret = acc_ctx_vfy_oid(minor_status, sc, &mechoid,
-				      negState, tokflag);
+				    negState, tokflag);
 		if (ret != GSS_S_COMPLETE)
 			return ret;
 	}
@@ -1772,6 +1955,10 @@
 		mechstat = GSS_S_CONTINUE_NEEDED;
 	}
 
+	/* Solaris SPNEGO */
+	if (*minor_status == ERR_SPNEGO_NEGOTIATION_FAILED)
+		spnego_gss_save_error_info(*minor_status, sc);
+
 	if (!HARD_ERROR(ret) && sc->mech_complete &&
 	    (sc->ctx_flags & GSS_C_INTEG_FLAG)) {
 
@@ -1872,16 +2059,30 @@
 		break;
 	    case ERR_SPNEGO_NEGOTIATION_FAILED:
 		/* CSTYLED */
-		*status_string = make_err_msg("SPNEGO failed to negotiate a mechanism");
-		break;
+		return(spnego_gss_display_status2(minor_status,
+						    status_value,
+						    status_type,
+						    mech_type,
+						    message_context,
+						    status_string));
 	    case ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR:
 		/* CSTYLED */
 		*status_string = make_err_msg("SPNEGO acceptor did not return a valid token");
 		break;
 	    default:
-		status_string->length = 0;
-		status_string->value = "";
-		break;
+		/*
+		 * Solaris SPNEGO
+		 * If mech_spnego calls mech_krb5 (via libgss) and an
+		 * error occurs there, give it a shot.
+		 */
+		/* CSTYLED */
+		return(krb5_gss_display_status2(minor_status,
+						status_value,
+						status_type,
+						(gss_OID)&gss_mech_krb5_oid,
+						message_context,
+						status_string));
+
 	}
 
 	dsyslog("Leaving display_status\n");
@@ -2593,7 +2794,7 @@
 			spnego_mechanism.mech_type.elements,
 			spnego_mechanism.mech_type.length)) {
 			/*
-			 * Solaris Kerberos: gss_indicate_mechs is stupid as
+			 * Solaris SPNEGO Kerberos: gss_indicate_mechs is stupid as
 			 * it never inferences any of the related OIDs of the
 			 * mechanisms configured, e.g. KRB5_OLD, KRB5_WRONG.
 			 * We add KRB5_WRONG here so that old MS clients can
@@ -3108,7 +3309,7 @@
 		gss_OID mech_oid = &mechset->elements[i];
 
 		/*
-		 * Solaris Kerberos: MIT compares against MS' wrong OID, but
+		 * Solaris SPNEGO Kerberos: MIT compares against MS' wrong OID, but
 		 * we actually want to select it if the client supports, as this
 		 * will enable features on MS clients that allow credential
 		 * refresh on rekeying and caching system times from servers.
@@ -3139,6 +3340,9 @@
 		}
 		return (returned_mech);
 	}
+	/* Solaris SPNEGO */
+	*minor_status= ERR_SPNEGO_NEGOTIATION_FAILED;
+
 	*negResult = REJECT;
 	return (NULL);
 }
--- a/usr/src/lib/libgss/Makefile.com	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/libgss/Makefile.com	Mon Aug 16 17:01:32 2010 -0700
@@ -19,8 +19,7 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
 #
 
 LIBRARY = libgss.a
@@ -70,19 +69,28 @@
 GSSCREDOBJ =	gsscred_utils.o gsscred_file.o
 # defines the duplicate sources we share with krb5 mech
 KRB5DIR= $(SRC)/lib/gss_mechs/mech_krb5/mech
-KRB5OBJ= rel_buffer.o util_buffer_set.o
+KRB5OBJ= rel_buffer.o util_buffer_set.o disp_com_err_status.o \
+	      util_buffer.o  util_errmap.o
+# defines the duplicate sources we share with krb5 mech error table
+KRB5ETDIR= $(SRC)/lib/gss_mechs/mech_krb5/et
+KRB5ETOBJ= error_message.o adb_err.o adm_err.o asn1_err.o \
+	      chpass_util_strings.o \
+	      gssapi_err_krb5.o gssapi_err_generic.o \
+	      import_err.o \
+	      kadm_err.o kdb5_err.o kdc5_err.o kpasswd_strings.o krb5_err.o \
+	      kv5m_err.o prof_err.o pty_err.o ss_err.o
 # defines the duplicate sources we share with kernel module
 UTSGSSDIR =	$(SRC)/uts/common/gssapi
 UTSGSSOBJ =	gen_oids.o
 
 SRCS +=		$(GSSCREDOBJ:%.o=$(GSSCRED_DIR)/%.c) \
 		$(KRB5OBJ:%.o=$(KRB5DIR)/%.c) \
+		$(KRB5ETOBJ:%.o=$(KRB5ETDIR)/%.c) \
 		$(UTSGSSOBJ:%.o=$(UTSGSSDIR)/%.c)
 GSSLINTSRC =	$(GSSOBJECTS:%.o=$(SRCDIR)/%.c) \
 		$(GSSCREDOBJ:%.o=$(GSSCRED_DIR)/%.c) \
-	        $(KRB5OBJ:%.o=$(KRB5DIR)/%.c) \
 		$(UTSGSSOBJ:%.o=$(UTSGSSDIR)/%.c)
-OBJECTS =	$(GSSOBJECTS) $(GSSCREDOBJ) $(KRB5OBJ) $(UTSGSSOBJ)
+OBJECTS =	$(GSSOBJECTS) $(GSSCREDOBJ) $(KRB5OBJ) $(UTSGSSOBJ) $(KRB5ETOBJ)
 
 # include library definitions
 include ../../Makefile.lib
@@ -113,13 +121,113 @@
 	$(POST_PROCESS_O)
 
 # we need this in libgss so we don't have to link against mech_krb5
-pics/rel_buffer.o: $(SRC)/lib/gss_mechs/mech_krb5/mech/rel_buffer.c
-	$(COMPILE.c) -o $@ $(SRC)/lib/gss_mechs/mech_krb5/mech/rel_buffer.c
+pics/rel_buffer.o: $(KRB5DIR)/rel_buffer.c
+	$(COMPILE.c) -o $@ $(KRB5DIR)/rel_buffer.c
+	$(POST_PROCESS_O)
+
+# we need this in libgss so we don't have to link against mech_krb5
+pics/util_buffer_set.o: $(KRB5DIR)/util_buffer_set.c
+	$(COMPILE.c) -o $@ $(KRB5DIR)/util_buffer_set.c
+	$(POST_PROCESS_O)
+
+# we need this in libgss so we don't have to link against mech_krb5
+pics/disp_com_err_status.o: $(KRB5DIR)/disp_com_err_status.c
+	$(COMPILE.c) -o $@ $(KRB5DIR)/disp_com_err_status.c
+	$(POST_PROCESS_O)
+
+# we need this in libgss so we don't have to link against mech_krb5
+pics/util_buffer.o: $(KRB5DIR)/util_buffer.c
+	$(COMPILE.c) -o $@ $(KRB5DIR)/util_buffer.c
+	$(POST_PROCESS_O)
+
+# we need this in libgss so we don't have to link against mech_krb5
+pics/util_errmap.o: $(KRB5DIR)/util_errmap.c
+	$(COMPILE.c) -o $@ $(KRB5DIR)/util_errmap.c
+	$(POST_PROCESS_O)
+
+# we need this in libgss so we don't have to link against mech_krb5
+pics/error_message.o: $(KRB5ETDIR)/error_message.c
+	$(COMPILE.c) -o $@ $(KRB5ETDIR)/error_message.c
+	$(POST_PROCESS_O)
+
+# we need this in libgss so we don't have to link against mech_krb5
+pics/adb_err.o: $(KRB5ETDIR)/adb_err.c
+	$(COMPILE.c) -o $@ $(KRB5ETDIR)/adb_err.c
+	$(POST_PROCESS_O)
+
+pics/adm_err.o: $(KRB5ETDIR)/adm_err.c
+	$(COMPILE.c) -o $@ $(KRB5ETDIR)/adm_err.c
+	$(POST_PROCESS_O)
+
+# we need this in libgss so we don't have to link against mech_krb5
+pics/asn1_err.o: $(KRB5ETDIR)/asn1_err.c
+	$(COMPILE.c) -o $@ $(KRB5ETDIR)/asn1_err.c
+	$(POST_PROCESS_O)
+
+# we need this in libgss so we don't have to link against mech_krb5
+pics/chpass_util_strings.o: $(KRB5ETDIR)/chpass_util_strings.c
+	$(COMPILE.c) -o $@ $(KRB5ETDIR)/chpass_util_strings.c
+	$(POST_PROCESS_O)
+
+# we need this in libgss so we don't have to link against mech_krb5
+pics/gssapi_err_generic.o: $(KRB5ETDIR)/gssapi_err_generic.c
+	$(COMPILE.c) -o $@ $(KRB5ETDIR)/gssapi_err_generic.c
 	$(POST_PROCESS_O)
 
 # we need this in libgss so we don't have to link against mech_krb5
-pics/util_buffer_set.o: $(SRC)/lib/gss_mechs/mech_krb5/mech/util_buffer_set.c
-	$(COMPILE.c) -o $@ $(SRC)/lib/gss_mechs/mech_krb5/mech/util_buffer_set.c
+pics/gssapi_err_krb5.o: $(KRB5ETDIR)/gssapi_err_krb5.c
+	$(COMPILE.c) -o $@ $(KRB5ETDIR)/gssapi_err_krb5.c
+	$(POST_PROCESS_O)
+
+
+# we need this in libgss so we don't have to link against mech_krb5
+pics/import_err.o: $(KRB5ETDIR)/import_err.c
+	$(COMPILE.c) -o $@ $(KRB5ETDIR)/import_err.c
+	$(POST_PROCESS_O)
+
+# we need this in libgss so we don't have to link against mech_krb5
+pics/kadm_err.o: $(KRB5ETDIR)/kadm_err.c
+	$(COMPILE.c) -o $@ $(KRB5ETDIR)/kadm_err.c
+	$(POST_PROCESS_O)
+
+# we need this in libgss so we don't have to link against mech_krb5
+pics/kdb5_err.o: $(KRB5ETDIR)/kdb5_err.c
+	$(COMPILE.c) -o $@ $(KRB5ETDIR)/kdb5_err.c
+	$(POST_PROCESS_O)
+
+# we need this in libgss so we don't have to link against mech_krb5
+pics/kdc5_err.o: $(KRB5ETDIR)/kdc5_err.c
+	$(COMPILE.c) -o $@ $(KRB5ETDIR)/kdc5_err.c
+	$(POST_PROCESS_O)
+
+# we need this in libgss so we don't have to link against mech_krb5
+pics/kpasswd_strings.o: $(KRB5ETDIR)/kpasswd_strings.c
+	$(COMPILE.c) -o $@ $(KRB5ETDIR)/kpasswd_strings.c
+	$(POST_PROCESS_O)
+
+# we need this in libgss so we don't have to link against mech_krb5
+pics/krb5_err.o: $(KRB5ETDIR)/krb5_err.c
+	$(COMPILE.c) -o $@ $(KRB5ETDIR)/krb5_err.c
+	$(POST_PROCESS_O)
+
+# we need this in libgss so we don't have to link against mech_krb5
+pics/kv5m_err.o: $(KRB5ETDIR)/kv5m_err.c
+	$(COMPILE.c) -o $@ $(KRB5ETDIR)/kv5m_err.c
+	$(POST_PROCESS_O)
+
+# we need this in libgss so we don't have to link against mech_krb5
+pics/prof_err.o: $(KRB5ETDIR)/prof_err.c
+	$(COMPILE.c) -o $@ $(KRB5ETDIR)/prof_err.c
+	$(POST_PROCESS_O)
+
+# we need this in libgss so we don't have to link against mech_krb5
+pics/pty_err.o: $(KRB5ETDIR)/pty_err.c
+	$(COMPILE.c) -o $@ $(KRB5ETDIR)/pty_err.c
+	$(POST_PROCESS_O)
+
+# we need this in libgss so we don't have to link against mech_krb5
+pics/ss_err.o: $(KRB5ETDIR)/ss_err.c
+	$(COMPILE.c) -o $@ $(KRB5ETDIR)/ss_err.c
 	$(POST_PROCESS_O)
 
 # gen_oids.c is kept in the kernel since the OIDs declared in them are
--- a/usr/src/lib/libgss/g_accept_sec_context.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/libgss/g_accept_sec_context.c	Mon Aug 16 17:01:32 2010 -0700
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
@@ -28,12 +27,15 @@
  */
 
 #include <mechglueP.h>
+#include "gssapiP_generic.h"
 #ifdef HAVE_STDLIB_H
 #include <stdlib.h>
 #endif
 #include <string.h>
 #include <errno.h>
+#include <syslog.h>
 
+#ifndef LEAN_CLIENT
 static OM_uint32
 val_acc_sec_ctx_args(
 	OM_uint32 *minor_status,
@@ -116,7 +118,6 @@
 	gss_name_t		tmp_src_name = GSS_C_NO_NAME;
 	gss_OID_desc	token_mech_type_desc;
 	gss_OID		token_mech_type = &token_mech_type_desc;
-	gss_OID		actual_mech = GSS_C_NO_OID;
 	OM_uint32	flags;
 	gss_mechanism	mech;
 
@@ -195,7 +196,7 @@
 					input_token_buffer,
 					input_chan_bindings,
 					&internal_name,
-					&actual_mech,
+					mech_type,
 					output_token,
 					&flags,
 					time_rec,
@@ -206,11 +207,15 @@
 			return (GSS_S_CONTINUE_NEEDED);
 
 		/* if the call failed, return with failure */
-		if (status != GSS_S_COMPLETE)
+		if (status != GSS_S_COMPLETE) {
+			if (mech_type && (*mech_type != GSS_C_NULL_OID))
+				map_error_oid(minor_status, *mech_type);
+			else {
+				map_error(minor_status, mech);
+			}
 			goto error_out;
+		}
 
-		if (mech_type != NULL)
-			*mech_type = actual_mech;
 
 		/*
 		 * if src_name is non-NULL,
@@ -225,6 +230,7 @@
 				internal_name, &tmp_src_name);
 			if (temp_status != GSS_S_COMPLETE) {
 				*minor_status = t_minstat;
+				map_error(minor_status, mech);
 				if (output_token->length)
 					(void) gss_release_buffer(
 						&t_minstat,
@@ -253,9 +259,9 @@
 			 * try to re-wrap it.  This is for SPNEGO or other
 			 * pseudo-mechanisms.
 			 */
-			if (actual_mech != GSS_C_NO_OID &&
+			if (*mech_type != GSS_C_NO_OID &&
 			    token_mech_type != GSS_C_NO_OID &&
-			    !g_OID_equal(actual_mech, token_mech_type)) {
+			    !g_OID_equal(*mech_type, token_mech_type)) {
 				*d_cred = tmp_d_cred;
 			} else {
 				gss_union_cred_t d_u_cred = NULL;
@@ -272,7 +278,7 @@
 
 				status = generic_gss_copy_oid(
 					&t_minstat,
-					actual_mech,
+					*mech_type,
 					&d_u_cred->mechs_array);
 
 				if (status != GSS_S_COMPLETE) {
@@ -312,6 +318,9 @@
 						NULL);
 				}
 
+				if (status != GSS_S_COMPLETE)
+					map_error(minor_status, mech);
+
 				if (internal_name != NULL) {
 					temp_status =
 					    __gss_convert_name_to_union_name(
@@ -319,6 +328,7 @@
 						internal_name, &tmp_src_name);
 					if (temp_status != GSS_S_COMPLETE) {
 						*minor_status = t_minstat;
+						map_error(minor_status, mech);
 						if (output_token->length)
 						    (void) gss_release_buffer(
 								&t_minstat,
@@ -382,3 +392,4 @@
 
 	return (status);
 }
+#endif /* LEAN_CLIENT */
--- a/usr/src/lib/libgss/g_acquire_cred.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/libgss/g_acquire_cred.c	Mon Aug 16 17:01:32 2010 -0700
@@ -19,14 +19,14 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
  *  glue routine for gss_acquire_cred
  */
 #include <mechglueP.h>
+#include "gssapiP_generic.h"
 #include <stdio.h>
 #ifdef HAVE_STDLIB_H
 #include <stdlib.h>
@@ -76,35 +76,52 @@
 
 static OM_uint32
 val_acq_cred_args(
-	OM_uint32 *minor_status,
-	gss_cred_id_t *output_cred_handle,
-	gss_OID_set *actual_mechs,
-	OM_uint32 *time_rec)
+    OM_uint32 *minor_status,
+    /*LINTED*/
+    gss_name_t desired_name,
+    /*LINTED*/
+    OM_uint32 time_req,
+    /*LINTED*/
+    gss_OID_set desired_mechs,
+    int cred_usage,
+    gss_cred_id_t *output_cred_handle,
+    gss_OID_set *actual_mechs,
+    OM_uint32 *time_rec)
 {
 
-	/* Initialize outputs. */
+   /* Initialize outputs. */
 
-	if (minor_status != NULL)
-		*minor_status = 0;
+    if (minor_status != NULL)
+        *minor_status = 0;
+
+    if (output_cred_handle != NULL)
+        *output_cred_handle = GSS_C_NO_CREDENTIAL;
 
-	if (output_cred_handle != NULL)
-		*output_cred_handle = GSS_C_NO_CREDENTIAL;
+    if (actual_mechs != NULL)
+        *actual_mechs = GSS_C_NULL_OID_SET;
 
-	if (actual_mechs != NULL)
-		*actual_mechs = GSS_C_NULL_OID_SET;
+    if (time_rec != NULL)
+        *time_rec = 0;
+
+    /* Validate arguments. */
 
-	if (time_rec != NULL)
-		*time_rec = 0;
+    if (minor_status == NULL)
+        return (GSS_S_CALL_INACCESSIBLE_WRITE);
 
-	/* Validate arguments. */
+    if (output_cred_handle == NULL)
+        return (GSS_S_CALL_INACCESSIBLE_WRITE);
 
-	if (minor_status == NULL)
-		return (GSS_S_CALL_INACCESSIBLE_WRITE);
+    if (cred_usage != GSS_C_ACCEPT
+        && cred_usage != GSS_C_INITIATE
+        && cred_usage != GSS_C_BOTH) {
+        if (minor_status) {
+            *minor_status = EINVAL;
+            map_errcode(minor_status);
+        }
+        return GSS_S_FAILURE;
+    }
 
-	if (output_cred_handle == NULL)
-		return (GSS_S_CALL_INACCESSIBLE_WRITE);
-
-	return (GSS_S_COMPLETE);
+    return (GSS_S_COMPLETE);
 }
 
 OM_uint32
@@ -133,10 +150,14 @@
 	gss_OID_set mechs;
 	gss_OID_desc default_OID;
 	gss_mechanism mech;
-	int i;
+	unsigned int i;
 	gss_union_cred_t creds;
 
 	major = val_acq_cred_args(minor_status,
+				desired_name,
+				time_req,
+				desired_mechs,
+				cred_usage,
 				output_cred_handle,
 				actual_mechs,
 				time_rec);
@@ -238,6 +259,15 @@
 val_add_cred_args(
 	OM_uint32 *minor_status,
 	gss_cred_id_t input_cred_handle,
+	/*LINTED*/
+	gss_name_t desired_name,
+	/*LINTED*/
+	gss_OID desired_mech,
+	gss_cred_usage_t cred_usage,
+	/*LINTED*/
+	OM_uint32 initiator_time_req,
+	/*LINTED*/
+	OM_uint32 acceptor_time_req,
 	gss_cred_id_t *output_cred_handle,
 	gss_OID_set *actual_mechs,
 	OM_uint32 *initiator_time_rec,
@@ -260,16 +290,24 @@
 
 	if (initiator_time_rec != NULL)
 		*initiator_time_rec = 0;
-
 	/* Validate arguments. */
 
 	if (minor_status == NULL)
 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
 
 	if (input_cred_handle == GSS_C_NO_CREDENTIAL &&
-		output_cred_handle == NULL)
+	    output_cred_handle == NULL)
+		return (GSS_S_CALL_INACCESSIBLE_WRITE | GSS_S_NO_CRED);
 
-		return (GSS_S_CALL_INACCESSIBLE_WRITE | GSS_S_NO_CRED);
+	if (cred_usage != GSS_C_ACCEPT
+	    && cred_usage != GSS_C_INITIATE
+	    && cred_usage != GSS_C_BOTH) {
+		if (minor_status) {
+			*minor_status = EINVAL;
+			map_errcode(minor_status);
+		}
+		return GSS_S_FAILURE;
+	}
 
 	return (GSS_S_COMPLETE);
 }
@@ -305,6 +343,11 @@
 
 	status = val_add_cred_args(minor_status,
 				input_cred_handle,
+				desired_name,
+				desired_mech,
+				cred_usage,
+				initiator_time_req,
+				acceptor_time_req,
 				output_cred_handle,
 				actual_mechs,
 				initiator_time_rec,
@@ -377,14 +420,18 @@
 	else if (cred_usage == GSS_C_BOTH)
 		time_req = (acceptor_time_req > initiator_time_req) ?
 			acceptor_time_req : initiator_time_req;
+	else
+		time_req = 0;
 
 	status = mech->gss_acquire_cred(mech->context, minor_status,
 				internal_name, time_req,
 				GSS_C_NULL_OID_SET, cred_usage,
 				&cred, NULL, &time_rec);
 
-	if (status != GSS_S_COMPLETE)
+	if (status != GSS_S_COMPLETE) {
+		map_error(minor_status, mech);
 		goto errout;
+	}
 
 	/* may need to set credential auxinfo structure */
 	if (union_cred->auxinfo.creation_time == 0) {
--- a/usr/src/lib/libgss/g_canon_name.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/libgss/g_canon_name.c	Mon Aug 16 17:01:32 2010 -0700
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
@@ -37,11 +36,13 @@
  */
 
 #include <mechglueP.h>
+#include "gssapiP_generic.h"
 #ifdef HAVE_STDLIB_H
 #include <stdlib.h>
 #endif
 #include <string.h>
 #include <errno.h>
+#include <syslog.h>
 
 static OM_uint32 val_canon_name_args(
 	OM_uint32 *minor_status,
@@ -81,6 +82,8 @@
 {
 	gss_union_name_t in_union, out_union = NULL, dest_union = NULL;
 	OM_uint32 major_status = GSS_S_FAILURE;
+	/* Solaris Kerberos - need to preserve more important minor_status */
+	OM_uint32 tmp_status = 0;
 
 	major_status = val_canon_name_args(minor_status,
 					input_name,
@@ -119,11 +122,14 @@
 			goto allocation_failure;
 
 		if (in_union->name_type != GSS_C_NULL_OID) {
-			if ((major_status = generic_gss_copy_oid(minor_status,
-				in_union->name_type, &out_union->name_type)))
-			goto allocation_failure;
+			major_status = generic_gss_copy_oid(minor_status,
+							in_union->name_type,
+							&out_union->name_type);
+			if (major_status) {
+				map_errcode(minor_status);
+				goto allocation_failure;
+			}
 		}
-
 	}
 
 	/*
@@ -145,13 +151,15 @@
 
 	/* now let's create the new mech name */
 	if (major_status = generic_gss_copy_oid(minor_status, mech_type,
-						&dest_union->mech_type))
+						&dest_union->mech_type)) {
+		map_errcode(minor_status);
 		goto allocation_failure;
+	}
 
 	if (major_status =
 		__gss_import_internal_name(minor_status, mech_type,
 						dest_union,
-						&dest_union->mech_name))
+					&dest_union->mech_name))
 		goto allocation_failure;
 
 	if (output_name)
@@ -159,6 +167,7 @@
 
 	return (GSS_S_COMPLETE);
 
+/* Solaris Kerberos - note some fails are not "allocation fails".  Sigh. */
 allocation_failure:
 	/* do not delete the src name external name format */
 	if (output_name) {
@@ -168,7 +177,7 @@
 			free(out_union->external_name);
 		}
 		if (out_union->name_type)
-			(void) gss_release_oid(minor_status,
+			(void) gss_release_oid(&tmp_status,
 					    &out_union->name_type);
 
 		dest_union = out_union;
@@ -181,13 +190,13 @@
 	 */
 
 	if (dest_union->mech_name) {
-		(void) __gss_release_internal_name(minor_status,
+		(void) __gss_release_internal_name(&tmp_status,
 						dest_union->mech_type,
 						&dest_union->mech_name);
 	}
 
 	if (dest_union->mech_type)
-		(void) gss_release_oid(minor_status, &dest_union->mech_type);
+		(void) gss_release_oid(&tmp_status, &dest_union->mech_type);
 
 
 	if (output_name)
--- a/usr/src/lib/libgss/g_compare_name.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/libgss/g_compare_name.c	Mon Aug 16 17:01:32 2010 -0700
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
@@ -29,6 +28,7 @@
  */
 
 #include <mechglueP.h>
+#include "gssapiP_generic.h"
 #ifdef HAVE_STDLIB_H
 #include <stdlib.h>
 #endif
@@ -72,7 +72,7 @@
 {
 	OM_uint32		major_status, temp_minor;
 	gss_union_name_t	union_name1, union_name2;
-	gss_mechanism		mech;
+	gss_mechanism		mech = NULL;
 	gss_name_t		internal_name;
 
 	major_status = val_comp_name_args(minor_status,
@@ -116,10 +116,18 @@
 			(union_name2->mech_name == 0))
 			/* should never happen */
 			return (GSS_S_BAD_NAME);
-		return (mech->gss_compare_name(mech->context, minor_status,
-							union_name1->mech_name,
-							union_name2->mech_name,
-							name_equal));
+		if (!mech)
+			return (GSS_S_BAD_MECH);
+		if (!mech->gss_compare_name)
+			return (GSS_S_UNAVAILABLE);
+		major_status = mech->gss_compare_name(mech->context,
+						    minor_status,
+						    union_name1->mech_name,
+						    union_name2->mech_name,
+						    name_equal);
+		if (major_status != GSS_S_COMPLETE)
+			map_error(minor_status, mech);
+		return major_status;
 	}
 
 	/*
@@ -189,10 +197,16 @@
 	if (major_status != GSS_S_COMPLETE)
 		return (GSS_S_COMPLETE); /* return complete, but not equal */
 
+	if (!mech)
+		return (GSS_S_BAD_MECH);
+	if (!mech->gss_compare_name)
+		return (GSS_S_UNAVAILABLE);
 	major_status = mech->gss_compare_name(mech->context, minor_status,
 							union_name1->mech_name,
 							internal_name,
 							name_equal);
+	if (major_status != GSS_S_COMPLETE)
+		map_error(minor_status, mech);
 	(void) __gss_release_internal_name(&temp_minor, union_name1->mech_type,
 					&internal_name);
 	return (major_status);
--- a/usr/src/lib/libgss/g_context_time.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/libgss/g_context_time.c	Mon Aug 16 17:01:32 2010 -0700
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -20,17 +19,15 @@
  * CDDL HEADER END
  */
 /*
- * Copyright (c) 1996,1997, by Sun Microsystems, Inc.
- * All rights reserved.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 /*
  *  glue routines for gss_context_time
  */
 
 #include <mechglueP.h>
+#include "gssapiP_generic.h"
 
 OM_uint32
 gss_context_time(minor_status,
@@ -65,13 +62,15 @@
 
 	if (mech) {
 
-		if (mech->gss_context_time)
+		if (mech->gss_context_time) {
 			status = mech->gss_context_time(
 							mech->context,
 							minor_status,
 							ctx->internal_ctx_id,
 							time_rec);
-		else
+			if (status != GSS_S_COMPLETE)
+				map_error(minor_status, mech);
+		} else
 			status = GSS_S_UNAVAILABLE;
 
 		return (status);
--- a/usr/src/lib/libgss/g_delete_sec_context.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/libgss/g_delete_sec_context.c	Mon Aug 16 17:01:32 2010 -0700
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
@@ -28,6 +27,7 @@
  */
 
 #include <mechglueP.h>
+#include "gssapiP_generic.h"
 #include <stdio.h>
 #ifdef HAVE_STDLIB_H
 #include <stdlib.h>
@@ -73,7 +73,6 @@
 {
 	OM_uint32		status;
 	gss_union_ctx_id_t	ctx;
-	gss_mechanism		mech;
 
 	status = val_del_sec_ctx_args(minor_status,
 				context_handle,
@@ -87,26 +86,21 @@
 	 */
 
 	ctx = (gss_union_ctx_id_t) *context_handle;
-	mech = __gss_get_mechanism(ctx->mech_type);
-
-	if (mech) {
-
-		if (mech->gss_delete_sec_context)
-			status = mech->gss_delete_sec_context(mech->context,
-							minor_status,
-							&ctx->internal_ctx_id,
-							output_token);
-		else
-			status = GSS_S_UNAVAILABLE;
+	if (GSSINT_CHK_LOOP(ctx))
+		return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT);
 
-		/* now free up the space for the union context structure */
-		free(ctx->mech_type->elements);
-		free(ctx->mech_type);
-		free(*context_handle);
-		*context_handle = NULL;
+	status = gssint_delete_internal_sec_context(minor_status,
+						    ctx->mech_type,
+						    &ctx->internal_ctx_id,
+						    output_token);
+	if (status)
+		return status;
 
-		return (status);
-	}
+	/* now free up the space for the union context structure */
+	free(ctx->mech_type->elements);
+	free(ctx->mech_type);
+	free(*context_handle);
+	*context_handle = GSS_C_NO_CONTEXT;
 
-	return (GSS_S_BAD_MECH);
+	return (GSS_S_COMPLETE);
 }
--- a/usr/src/lib/libgss/g_dsp_name.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/libgss/g_dsp_name.c	Mon Aug 16 17:01:32 2010 -0700
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
@@ -29,6 +28,7 @@
  */
 
 #include <mechglueP.h>
+#include "gssapiP_generic.h"
 #include <stdio.h>
 #ifdef HAVE_STDLIB_H
 #include <stdlib.h>
@@ -113,8 +113,10 @@
 		major_status = generic_gss_copy_oid(minor_status,
 						union_name->name_type,
 						output_name_type);
-		if (major_status != GSS_S_COMPLETE)
+		if (major_status != GSS_S_COMPLETE) {
+			map_errcode(minor_status);
 			return (major_status);
+		}
 	}
 
 	if ((output_name_buffer->value =
--- a/usr/src/lib/libgss/g_dsp_status.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/libgss/g_dsp_status.c	Mon Aug 16 17:01:32 2010 -0700
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
@@ -29,6 +28,7 @@
  */
 
 #include <mechglueP.h>
+#include "gssapiP_generic.h"
 #include <stdio.h>
 #ifdef HAVE_STDLIB_H
 #include <stdlib.h>
@@ -36,7 +36,7 @@
 #include <string.h>
 #include <libintl.h>
 #include <errno.h>
-
+#include <syslog.h>
 #ifndef TEXT_DOMAIN
 #error TEXT_DOMAIN not defined
 #endif
@@ -60,8 +60,9 @@
 OM_uint32 *message_context;
 gss_buffer_t status_string;
 {
-	gss_OID mech_type = (gss_OID) req_mech_type;
-	gss_mechanism mech;
+	gss_OID			mech_type = (gss_OID) req_mech_type;
+	gss_mechanism		mech;
+	gss_OID_desc		m_oid = { 0, 0 };
 
 	if (minor_status != NULL)
 		*minor_status = 0;
@@ -75,7 +76,7 @@
 	    message_context == NULL ||
 	    status_string == GSS_C_NO_BUFFER)
 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
-
+	
 	/* we handle major status codes, and the mechs do the minor */
 	if (status_type == GSS_C_GSS_CODE)
 		return (displayMajor(status_value, message_context,
@@ -86,15 +87,63 @@
 	 * select the appropriate underlying mechanism routine and
 	 * call it.
 	 */
+
+	/* In this version, we only handle status codes that have been
+	   mapped to a flat numbering space.  Look up the value we got
+	   passed.  If it's not found, complain.  */
+	if (status_value == 0) {
+		status_string->value = strdup("Unknown error");
+		if (status_string->value == NULL) {
+			*minor_status = ENOMEM;
+			map_errcode(minor_status);
+			return GSS_S_FAILURE;
+		}
+		status_string->length = strlen(status_string->value);
+		*message_context = 0;
+		*minor_status = 0;
+		return GSS_S_COMPLETE;
+	}
+	{
+		int err;
+	        OM_uint32 m_status = 0, status;
+
+		err = gssint_mecherrmap_get(status_value, &m_oid, &m_status);
+		if (err) {
+			*minor_status = err;
+			map_errcode(minor_status);
+			return GSS_S_BAD_STATUS;
+		}
+
+		if (m_oid.length == 0) {
+			/* Magic flag for com_err values.  */
+			status = gssint_g_display_com_err_status(minor_status,
+							m_status,
+							status_string);
+			if (status != GSS_S_COMPLETE)
+				map_errcode(minor_status);
+			return status;
+		}
+		mech_type = &m_oid;
+		status_value = m_status;
+	}
+
 	mech = __gss_get_mechanism(mech_type);
 
 	if (mech && mech->gss_display_status) {
+		OM_uint32 r;
+
 		if (mech_type == GSS_C_NULL_OID)
 			mech_type = &mech->mech_type;
 
-		return (mech->gss_display_status(mech->context, minor_status,
+		r = mech->gss_display_status(mech->context, minor_status,
 				status_value, status_type, mech_type,
-				message_context, status_string));
+				message_context, status_string);
+		/* How's this for weird?  If we get an error returning the
+		mechanism-specific error code, we save away the
+		mechanism-specific error code describing the error.  */
+		if (r != GSS_S_COMPLETE)
+			map_error(minor_status, mech);
+		return r;
 	}
 
 	if (!mech)
@@ -354,12 +403,11 @@
 
 	/* now copy the status code and return to caller */
 	outStr->length = strlen(errStr);
-	outStr->value = malloc((size_t)outStr->length+1);
+	outStr->value = strdup(errStr);
 	if (outStr->value == NULL) {
 		outStr->length = 0;
 		return (GSS_S_FAILURE);
 	}
 
-	(void) strcpy((char *)outStr->value, errStr);
 	return (GSS_S_COMPLETE);
 } /* displayMajor */
--- a/usr/src/lib/libgss/g_dup_name.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/libgss/g_dup_name.c	Mon Aug 16 17:01:32 2010 -0700
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
@@ -31,6 +30,7 @@
  */
 
 #include <mechglueP.h>
+#include "gssapiP_generic.h"
 #ifdef HAVE_STDLIB_H
 #include <stdlib.h>
 #endif
@@ -108,8 +108,10 @@
 		major_status = generic_gss_copy_oid(minor_status,
 						src_union->name_type,
 						&dest_union->name_type);
-		if (major_status != GSS_S_COMPLETE)
+		if (major_status != GSS_S_COMPLETE) {
+			map_errcode(minor_status);
 			goto allocation_failure;
+		}
 	}
 
 	/*
@@ -119,8 +121,10 @@
 		major_status = generic_gss_copy_oid(minor_status,
 							src_union->mech_type,
 							&dest_union->mech_type);
-		if (major_status != GSS_S_COMPLETE)
+		if (major_status != GSS_S_COMPLETE) {
+			map_errcode(minor_status);
 			goto allocation_failure;
+		}
 
 		major_status = __gss_import_internal_name(minor_status,
 							dest_union->mech_type,
--- a/usr/src/lib/libgss/g_exp_sec_context.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/libgss/g_exp_sec_context.c	Mon Aug 16 17:01:32 2010 -0700
@@ -19,15 +19,16 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
  *  glue routine for gss_export_sec_context
  */
+#ifndef LEAN_CLIENT
 
 #include <mechglueP.h>
+#include "gssapiP_generic.h"
 #include <stdio.h>
 #include <errno.h>
 #ifdef HAVE_STDLIB_H
@@ -101,8 +102,10 @@
 
 	status = mech->gss_export_sec_context(mech->context, minor_status,
 					&ctx->internal_ctx_id, &token);
-	if (status != GSS_S_COMPLETE)
+	if (status != GSS_S_COMPLETE) {
+		map_error(minor_status, mech);
 		return (status);
+	}
 
 	length = token.length + 4 + ctx->mech_type->length;
 	interprocess_token->length = length;
@@ -133,3 +136,4 @@
 
 	return (GSS_S_COMPLETE);
 }
+#endif /*LEAN_CLIENT */
--- a/usr/src/lib/libgss/g_glue.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/libgss/g_glue.c	Mon Aug 16 17:01:32 2010 -0700
@@ -3,7 +3,7 @@
  */
 
 #include "mglueP.h"
-
+#include "gssapiP_generic.h"
 #include <stdio.h>
 #ifdef HAVE_STDLIB_H
 #include <stdlib.h>
--- a/usr/src/lib/libgss/g_imp_name.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/libgss/g_imp_name.c	Mon Aug 16 17:01:32 2010 -0700
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
@@ -29,6 +28,7 @@
  */
 
 #include <mechglueP.h>
+#include "gssapiP_generic.h"
 #include <stdio.h>
 #ifdef HAVE_STDLIB_H
 #include <stdlib.h>
@@ -125,8 +125,10 @@
 		major_status = generic_gss_copy_oid(minor_status,
 						input_name_type,
 						&union_name->name_type);
-		if (major_status != GSS_S_COMPLETE)
+		if (major_status != GSS_S_COMPLETE) {
+			map_errcode(minor_status);
 			goto allocation_failure;
+		}
 	}
 
 	/*
@@ -250,13 +252,17 @@
 	 * have created it.
 	 */
 	if (mech->gss_export_name) {
-		if ((major = mech->gss_import_name(mech->context, minor,
-				&expName, (gss_OID)GSS_C_NT_EXPORT_NAME,
-				&unionName->mech_name)) != GSS_S_COMPLETE ||
-			(major = generic_gss_copy_oid(minor, &mechOid,
-					&unionName->mech_type)) !=
-				GSS_S_COMPLETE) {
-			return (major);
+		major = mech->gss_import_name(mech->context, minor,
+					    &expName,
+					    (gss_OID)GSS_C_NT_EXPORT_NAME,
+					    &unionName->mech_name);
+		if (major != GSS_S_COMPLETE)
+			map_error(minor, mech);
+		else {
+			major = generic_gss_copy_oid(minor, &mechOid,
+						    &unionName->mech_type);
+			if (major != GSS_S_COMPLETE)
+				map_errcode(minor);
 		}
 		return (major);
 	}
@@ -349,8 +355,14 @@
 	expName.value = nameLen ? (void *)buf : NULL;
 	major = mech->gss_import_name(mech->context, minor, &expName,
 			    GSS_C_NULL_OID, &unionName->mech_name);
-	if (major != GSS_S_COMPLETE)
+	if (major != GSS_S_COMPLETE) {
+		map_error(minor, mech);
 		return (major);
+	}
 
-	return (generic_gss_copy_oid(minor, &mechOid, &unionName->mech_type));
+	major = generic_gss_copy_oid(minor, &mechOid, &unionName->mech_type);
+	if (major != GSS_S_COMPLETE) {
+		map_errcode(minor);
+	}
+	return (major);
 } /* importExportName */
--- a/usr/src/lib/libgss/g_imp_sec_context.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/libgss/g_imp_sec_context.c	Mon Aug 16 17:01:32 2010 -0700
@@ -19,15 +19,17 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
  *  glue routine gss_export_sec_context
  */
 
+#ifndef LEAN_CLIENT
+
 #include <mechglueP.h>
+#include "gssapiP_generic.h"
 #include <stdio.h>
 #include <errno.h>
 #include <stdlib.h>
@@ -146,6 +148,7 @@
 		*context_handle = (gss_ctx_id_t)ctx;
 		return (GSS_S_COMPLETE);
 	}
+	map_error(minor_status, mech);
 
 error_out:
 	if (ctx) {
@@ -158,3 +161,4 @@
 	}
 	return (status);
 }
+#endif /* LEAN_CLIENT */
--- a/usr/src/lib/libgss/g_init_sec_context.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/libgss/g_init_sec_context.c	Mon Aug 16 17:01:32 2010 -0700
@@ -19,14 +19,14 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
  *  glue routine for gss_init_sec_context
  */
 #include <mechglueP.h>
+#include "gssapiP_generic.h"
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -211,6 +211,7 @@
 		 * subsequent calls make the caller responsible for
 		 * calling gss_delete_sec_context
 		 */
+		map_error(minor_status, mech);
 		if (*context_handle == GSS_C_NO_CONTEXT) {
 			free(union_ctx_id->mech_type->elements);
 			free(union_ctx_id->mech_type);
--- a/usr/src/lib/libgss/g_initialize.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/libgss/g_initialize.c	Mon Aug 16 17:01:32 2010 -0700
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
@@ -42,6 +41,7 @@
  */
 
 #include <mechglueP.h>
+#include "gssapiP_generic.h"
 #include <stdio.h>
 #include <syslog.h>
 #include <stdlib.h>
@@ -154,6 +154,7 @@
 					minor_status, oid);
 			if (major == GSS_S_COMPLETE)
 				return (GSS_S_COMPLETE);
+			map_error(minor_status, aMech->mech);
 		}
 		aMech = aMech->next;
 	} /* while */
--- a/usr/src/lib/libgss/g_inq_context_oid.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/libgss/g_inq_context_oid.c	Mon Aug 16 17:01:32 2010 -0700
@@ -23,8 +23,7 @@
  *
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
@@ -32,6 +31,8 @@
  */
 
 #include "mglueP.h"
+#include "gssapiP_generic.h"
+
 #define gssint_get_mechanism __gss_get_mechanism /* SUNW17PACresync */
 
 OM_uint32
--- a/usr/src/lib/libgss/g_inquire_context.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/libgss/g_inquire_context.c	Mon Aug 16 17:01:32 2010 -0700
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
@@ -28,6 +27,7 @@
  */
 
 #include <mechglueP.h>
+#include "gssapiP_generic.h"
 #include <stdlib.h>
 
 static OM_uint32
@@ -67,26 +67,15 @@
 /* Last argument new for V2 */
 OM_uint32
 gss_inquire_context(
-		minor_status,
-		context_handle,
-		src_name,
-		targ_name,
-		lifetime_rec,
-		mech_type,
-		ctx_flags,
-		locally_initiated,
-		open)
-
-OM_uint32 *minor_status;
-const gss_ctx_id_t context_handle;
-gss_name_t *src_name;
-gss_name_t *targ_name;
-OM_uint32 *lifetime_rec;
-gss_OID *mech_type;
-OM_uint32 *ctx_flags;
-int *locally_initiated;
-int *open;
-
+          OM_uint32 *minor_status,
+          gss_ctx_id_t context_handle,
+          gss_name_t *src_name,
+          gss_name_t *targ_name,
+          OM_uint32 *lifetime_rec,
+          gss_OID *mech_type,
+          OM_uint32 *ctx_flags,
+          int *locally_initiated,
+          int *opened)
 {
 	gss_union_ctx_id_t	ctx;
 	gss_mechanism		mech;
@@ -124,9 +113,10 @@
 				NULL,
 				ctx_flags,
 				locally_initiated,
-				open);
+				opened);
 
 	if (status != GSS_S_COMPLETE) {
+		map_error(minor_status, mech);
 		return (status);
 	}
 
--- a/usr/src/lib/libgss/g_inquire_cred.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/libgss/g_inquire_cred.c	Mon Aug 16 17:01:32 2010 -0700
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
@@ -28,6 +27,7 @@
  */
 
 #include <mechglueP.h>
+#include "gssapiP_generic.h"
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -91,8 +91,10 @@
 						lifetime, cred_usage,
 						mechanisms);
 
-		if (status != GSS_S_COMPLETE)
+		if (status != GSS_S_COMPLETE) {
+			map_error(minor_status, mech);
 			return (status);
+		}
 
 		if (name) {
 		/*
@@ -103,6 +105,7 @@
 						internal_name, name);
 			if (status != GSS_S_COMPLETE) {
 				*minor_status = temp_minor_status;
+				map_error(minor_status, mech);
 				if (mechanisms && *mechanisms) {
 					(void) gss_release_oid_set(
 						&temp_minor_status,
@@ -144,7 +147,9 @@
 	 */
 
 	if (name != NULL) {
-		if ((gss_import_name(minor_status,
+		if (union_cred->auxinfo.name.length == 0) {
+			*name = GSS_C_NO_NAME;
+		} else if ((gss_import_name(minor_status,
 					&union_cred->auxinfo.name,
 					union_cred->auxinfo.name_type,
 					name) != GSS_S_COMPLETE) ||
@@ -251,8 +256,10 @@
 					initiator_lifetime,
 					acceptor_lifetime, cred_usage);
 
-		if (status != GSS_S_COMPLETE)
+		if (status != GSS_S_COMPLETE) {
+			map_error(minor_status, mech);
 			return (status);
+		}
 
 		if (name) {
 			/*
@@ -263,6 +270,7 @@
 					internal_name, name);
 			if (status != GSS_S_COMPLETE) {
 				*minor_status = temp_minor_status;
+				map_error(minor_status, mech);
 				return (status);
 			}
 		}
--- a/usr/src/lib/libgss/g_inquire_names.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/libgss/g_inquire_names.c	Mon Aug 16 17:01:32 2010 -0700
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
@@ -28,6 +27,7 @@
  */
 
 #include <mechglueP.h>
+#include "gssapiP_generic.h"
 
 #define	MAX_MECH_OID_PAIRS 32
 
@@ -68,13 +68,15 @@
 
 	if (mech) {
 
-		if (mech->gss_inquire_names_for_mech)
+		if (mech->gss_inquire_names_for_mech) {
 			status = mech->gss_inquire_names_for_mech(
 					mech->context,
 					minor_status,
 					mechanism,
 					name_types);
-		else
+			if (status != GSS_S_COMPLETE)
+				map_error(minor_status, mech);
+		} else
 			status = GSS_S_UNAVAILABLE;
 
 		return (status);
--- a/usr/src/lib/libgss/g_oid_ops.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/libgss/g_oid_ops.c	Mon Aug 16 17:01:32 2010 -0700
@@ -1,8 +1,6 @@
 /*
- * Copyright (c) 1996,1997, by Sun Microsystems, Inc.
- * All rights reserved.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
 /*
  * lib/gssapi/mechglue/g_oid_ops.c
  *
@@ -32,6 +30,7 @@
  */
 
 #include <mechglueP.h>
+#include "gssapiP_generic.h"
 
 /*
  * gss_release_oid has been moved to g_initialize, becasue it requires access
@@ -44,8 +43,11 @@
 	OM_uint32		*minor_status;
 	gss_OID_set		*oid_set;
 {
-		return (generic_gss_create_empty_oid_set(minor_status,
-				oid_set));
+	OM_uint32 status;
+	status = generic_gss_create_empty_oid_set(minor_status, oid_set);
+	if (status != GSS_S_COMPLETE)
+		map_errcode(minor_status);
+	return status;
 }
 
 OM_uint32
@@ -54,8 +56,12 @@
 	const gss_OID		member_oid;
 	gss_OID_set		*oid_set;
 {
-	return (generic_gss_add_oid_set_member(minor_status, member_oid,
-				oid_set));
+	OM_uint32 status;
+	status = generic_gss_add_oid_set_member(minor_status, member_oid,
+						oid_set);
+	if (status != GSS_S_COMPLETE)
+		map_errcode(minor_status);
+	return status;
 }
 
 OM_uint32
@@ -75,7 +81,10 @@
 	const gss_OID		oid;
 	gss_buffer_t		oid_str;
 {
-	return (generic_gss_oid_to_str(minor_status, oid, oid_str));
+	OM_uint32 status = generic_gss_oid_to_str(minor_status, oid, oid_str);
+	if (status != GSS_S_COMPLETE)
+		map_errcode(minor_status);
+	return status;
 }
 
 OM_uint32
@@ -84,5 +93,8 @@
 	const gss_buffer_t	oid_str;
 	gss_OID			*oid;
 {
-	return (generic_gss_str_to_oid(minor_status, oid_str, oid));
+	OM_uint32 status = generic_gss_str_to_oid(minor_status, oid_str, oid);
+	if (status != GSS_S_COMPLETE)
+		map_errcode(minor_status);
+	return status;
 }
--- a/usr/src/lib/libgss/g_process_context.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/libgss/g_process_context.c	Mon Aug 16 17:01:32 2010 -0700
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
@@ -28,6 +27,7 @@
  */
 
 #include <mechglueP.h>
+#include "gssapiP_generic.h"
 
 OM_uint32
 gss_process_context_token(minor_status,
@@ -66,13 +66,15 @@
 
 	if (mech) {
 
-		if (mech->gss_process_context_token)
+		if (mech->gss_process_context_token) {
 			status = mech->gss_process_context_token(
 							mech->context,
 							minor_status,
 							ctx->internal_ctx_id,
 							token_buffer);
-		else
+			if (status != GSS_S_COMPLETE)
+				map_error(minor_status, mech);
+		} else
 			status = GSS_S_UNAVAILABLE;
 
 		return (status);
--- a/usr/src/lib/libgss/g_rel_cred.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/libgss/g_rel_cred.c	Mon Aug 16 17:01:32 2010 -0700
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -20,17 +19,15 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 /*
  *  glue routine for gss_release_cred
  */
 
 #include <mechglueP.h>
+#include "gssapiP_generic.h"
 #include <stdio.h>
 #ifdef HAVE_STDLIB_H
 #include <stdlib.h>
@@ -64,11 +61,14 @@
 	 */
 
 	union_cred = (gss_union_cred_t)*cred_handle;
-	*cred_handle = NULL;
-
 	if (union_cred == (gss_union_cred_t)GSS_C_NO_CREDENTIAL)
 		return (GSS_S_COMPLETE);
 
+	if (GSSINT_CHK_LOOP(union_cred))
+		return (GSS_S_NO_CRED | GSS_S_CALL_INACCESSIBLE_READ);
+
+	*cred_handle = NULL;
+
 	status = GSS_S_COMPLETE;
 
 	for (j = 0; j < union_cred->count; j++) {
@@ -83,8 +83,10 @@
 						(mech->context, minor_status,
 						&union_cred->cred_array[j]);
 
-				if (temp_status != GSS_S_COMPLETE)
+				if (temp_status != GSS_S_COMPLETE) {
+					map_error(minor_status, mech);
 					status = GSS_S_NO_CRED;
+				}
 			} else
 				status = GSS_S_UNAVAILABLE;
 		} else
--- a/usr/src/lib/libgss/g_seal.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/libgss/g_seal.c	Mon Aug 16 17:01:32 2010 -0700
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
@@ -28,6 +27,8 @@
  */
 
 #include <mechglueP.h>
+#include "gssapiP_generic.h"
+
 
 static OM_uint32
 val_seal_args(
@@ -104,7 +105,7 @@
 	mech = __gss_get_mechanism(ctx->mech_type);
 
 	if (mech) {
-		if (mech->gss_seal)
+		if (mech->gss_seal) {
 			status = mech->gss_seal(
 						mech->context,
 						minor_status,
@@ -114,7 +115,9 @@
 						input_message_buffer,
 						conf_state,
 						output_message_buffer);
-		else
+			if (status != GSS_S_COMPLETE)
+				map_error(minor_status, mech);
+		} else
 			status = GSS_S_UNAVAILABLE;
 
 		return (status);
@@ -163,6 +166,7 @@
 {
 	gss_union_ctx_id_t	ctx;
 	gss_mechanism		mech;
+	OM_uint32         major_status;
 
 	if (minor_status == NULL)
 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
@@ -185,10 +189,16 @@
 	if (!mech)
 		return (GSS_S_BAD_MECH);
 
-	if (!mech->gss_wrap_size_limit)
-		return (GSS_S_UNAVAILABLE);
-
-	return (mech->gss_wrap_size_limit(mech->context, minor_status,
-				ctx->internal_ctx_id, conf_req_flag, qop_req,
-				req_output_size, max_input_size));
+	if (mech->gss_wrap_size_limit)
+		major_status = mech->gss_wrap_size_limit(mech->context,
+							minor_status,
+							ctx->internal_ctx_id,
+							conf_req_flag, qop_req,
+							req_output_size,
+							max_input_size);
+	else
+		major_status = GSS_S_UNAVAILABLE;
+	if (major_status != GSS_S_COMPLETE)
+		map_error(minor_status, mech);
+	return major_status;
 }
--- a/usr/src/lib/libgss/g_sign.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/libgss/g_sign.c	Mon Aug 16 17:01:32 2010 -0700
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
@@ -28,6 +27,7 @@
  */
 
 #include <mechglueP.h>
+#include "gssapiP_generic.h"
 
 static OM_uint32
 val_sign_args(
@@ -96,7 +96,7 @@
 	mech = __gss_get_mechanism(ctx->mech_type);
 
 	if (mech) {
-		if (mech->gss_sign)
+		if (mech->gss_sign) {
 			status = mech->gss_sign(
 						mech->context,
 						minor_status,
@@ -104,7 +104,9 @@
 						qop_req,
 						message_buffer,
 						msg_token);
-		else
+			if (status != GSS_S_COMPLETE)
+				map_error(minor_status, mech);
+		} else
 			status = GSS_S_UNAVAILABLE;
 
 		return (status);
--- a/usr/src/lib/libgss/g_store_cred.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/libgss/g_store_cred.c	Mon Aug 16 17:01:32 2010 -0700
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
@@ -28,28 +27,50 @@
  */
 
 #include <mechglueP.h>
+#include "gssapiP_generic.h"
+#include <errno.h>
 
-static OM_uint32 val_store_cred_args(
-	OM_uint32 *minor_status,
-	const gss_cred_id_t input_cred_handle,
-	gss_OID_set *elements_stored)
+static OM_uint32
+val_store_cred_args(
+        OM_uint32 *minor_status,
+        const gss_cred_id_t input_cred_handle,
+        gss_cred_usage_t cred_usage,
+	/*LINTED*/
+        const gss_OID desired_mech,
+	/*LINTED*/
+        OM_uint32 overwrite_cred,
+	/*LINTED*/
+        OM_uint32 default_cred,
+        gss_OID_set *elements_stored,
+	/*LINTED*/
+        gss_cred_usage_t *cred_usage_stored)
 {
 
-	/* Initialize outputs. */
+        /* Initialize outputs. */
+
+        if (minor_status != NULL)
+                *minor_status = 0;
 
-	if (minor_status != NULL)
-		*minor_status = 0;
+        if (elements_stored != NULL)
+                *elements_stored = GSS_C_NULL_OID_SET;
 
-	if (elements_stored != NULL)
-		*elements_stored = GSS_C_NULL_OID_SET;
+       /* Validate arguments. */
+
+        if (minor_status == NULL)
+                return (GSS_S_CALL_INACCESSIBLE_WRITE);
 
-	/* Validate arguments. */
+        if (input_cred_handle == GSS_C_NO_CREDENTIAL)
+                return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CRED);
 
-	if (minor_status == NULL)
-		return (GSS_S_CALL_INACCESSIBLE_WRITE);
-
-	if (input_cred_handle == GSS_C_NO_CREDENTIAL)
-		return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CRED);
+	if (cred_usage != GSS_C_ACCEPT
+	    && cred_usage != GSS_C_INITIATE
+	    && cred_usage != GSS_C_BOTH) {
+		if (minor_status) {
+			*minor_status = EINVAL;
+			map_errcode(minor_status);
+		}
+		return GSS_S_FAILURE;
+	}
 
 	return (GSS_S_COMPLETE);
 }
@@ -80,9 +101,14 @@
 	gss_OID			dmech;
 	int			i;
 
-	major_status = val_store_cred_args(minor_status,
-					input_cred_handle,
-					elements_stored);
+        major_status = val_store_cred_args(minor_status,
+                                           input_cred_handle,
+                                           cred_usage,
+                                           desired_mech,
+                                           overwrite_cred,
+                                           default_cred,
+                                           elements_stored,
+                                           cred_usage_stored);
 	if (major_status != GSS_S_COMPLETE)
 		return (major_status);
 
@@ -107,7 +133,7 @@
 		if (mech_cred == GSS_C_NO_CREDENTIAL)
 			return (GSS_S_NO_CRED);
 
-		return (mech->gss_store_cred(mech->context,
+		major_status = mech->gss_store_cred(mech->context,
 						minor_status,
 						(gss_cred_id_t)mech_cred,
 						cred_usage,
@@ -115,7 +141,10 @@
 						overwrite_cred,
 						default_cred,
 						elements_stored,
-						cred_usage_stored));
+						    cred_usage_stored);
+		if (major_status != GSS_S_COMPLETE)
+			map_error(minor_status, mech);
+		return major_status;
 	}
 
 	/* desired_mech == GSS_C_NULL_OID -> store all elements */
@@ -145,8 +174,10 @@
 						default_cred,
 						NULL,
 						cred_usage_stored);
-		if (major_status != GSS_S_COMPLETE)
+		if (major_status != GSS_S_COMPLETE) {
+			map_error(minor_status, mech);
 			continue;
+		}
 
 		/* Succeeded for at least one mech */
 
--- a/usr/src/lib/libgss/g_unseal.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/libgss/g_unseal.c	Mon Aug 16 17:01:32 2010 -0700
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
@@ -28,6 +27,7 @@
  */
 
 #include <mechglueP.h>
+#include "gssapiP_generic.h"
 
 OM_uint32
 gss_unseal(minor_status,
@@ -80,7 +80,7 @@
 	mech = __gss_get_mechanism(ctx->mech_type);
 
 	if (mech) {
-		if (mech->gss_unseal)
+		if (mech->gss_unseal) {
 			status = mech->gss_unseal(
 						mech->context,
 						minor_status,
@@ -89,7 +89,9 @@
 						output_message_buffer,
 						conf_state,
 						qop_state);
-		else
+			if (status != GSS_S_COMPLETE)
+				map_error(minor_status, mech);
+		} else
 			status = GSS_S_UNAVAILABLE;
 
 		return (status);
--- a/usr/src/lib/libgss/g_verify.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/lib/libgss/g_verify.c	Mon Aug 16 17:01:32 2010 -0700
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
@@ -28,6 +27,7 @@
  */
 
 #include <mechglueP.h>
+#include "gssapiP_generic.h"
 
 OM_uint32
 gss_verify(minor_status,
@@ -66,7 +66,7 @@
 	mech = __gss_get_mechanism(ctx->mech_type);
 
 	if (mech) {
-		if (mech->gss_verify)
+		if (mech->gss_verify) {
 			status = mech->gss_verify(
 						mech->context,
 						minor_status,
@@ -74,7 +74,9 @@
 						message_buffer,
 						token_buffer,
 						qop_state);
-		else
+			if (status != GSS_S_COMPLETE)
+				map_error(minor_status, mech);
+		} else
 			status = GSS_S_UNAVAILABLE;
 
 		return (status);
--- a/usr/src/uts/common/gssapi/include/mechglueP.h	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/uts/common/gssapi/include/mechglueP.h	Mon Aug 16 17:01:32 2010 -0700
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
@@ -41,7 +40,9 @@
 #endif
 
 #include <gssapi/gssapi_ext.h>   /* SUNW15resync - mechglue.h in mit 1.5 */
-/* #include "gssapiP_generic.h" */
+#if 0 /* Solaris Kerberos */
+#include "gssapiP_generic.h"
+#endif
 
 #ifdef _KERNEL
 #include <rpc/rpc.h>
@@ -126,6 +127,18 @@
 		    OM_uint32 *		/* time_rec */
 	/* */);
 
+/*
+ * Rudimentary pointer validation macro to check whether the
+ * "loopback" field of an opaque struct points back to itself.  This
+ * field also catches some programming errors where an opaque pointer
+ * is passed to a function expecting the address of the opaque
+ * pointer.
+ */
+#if 0 /* Solaris Kerberos - revisit for full 1.7/next resync */
+#define GSSINT_CHK_LOOP(p) (!((p) != NULL && (p)->loopback == (p)))
+#else
+#define GSSINT_CHK_LOOP(p) ((p) == NULL)
+#endif
 
 
 /********************************************************/
@@ -580,6 +593,8 @@
 	const gss_name_t, gss_buffer_t, gss_OID *);
 OM_uint32 __gss_release_internal_name(OM_uint32 *, const gss_OID,
 	gss_name_t *);
+OM_uint32 gssint_delete_internal_sec_context (OM_uint32 *, gss_OID,
+	gss_ctx_id_t *, gss_buffer_t);
 OM_uint32 __gss_convert_name_to_union_name(
 	OM_uint32 *,		/* minor_status */
 	gss_mechanism,	/* mech */
@@ -928,16 +943,27 @@
 
 #else  /* _KERNEL */
 
-#include <syslog.h>
+/* Use this to map an error code that was returned from a mech
+   operation; the mech will be asked to produce the associated error
+   messages.
 
-#define map_error(MINORP, MECH)				\
-	(void) syslog(LOG_AUTH|LOG_DEBUG,		\
-		    "map_error: minor status=%x",	\
-		    (MINORP) ? *(MINORP) : 0xffffffff)
+   Remember that if the minor status code cannot be returned to the
+   caller (e.g., if it's stuffed in an automatic variable and then
+   ignored), then we don't care about producing a mapping.  */
+#define map_error(MINORP, MECH) \
+    (*(MINORP) = gssint_mecherrmap_map(*(MINORP), &(MECH)->mech_type))
+#define map_error_oid(MINORP, MECHOID) \
+    (*(MINORP) = gssint_mecherrmap_map(*(MINORP), (MECHOID)))
+
+/* Use this to map an errno value or com_err error code being
+   generated within the mechglue code (e.g., by calling generic oid
+   ops).  Any errno or com_err values produced by mech operations
+   should be processed with map_error.  This means they'll be stored
+   separately even if the mech uses com_err, because we can't assume
+   that it will use com_err.  */
 #define map_errcode(MINORP) \
-	(void) syslog(LOG_AUTH|LOG_DEBUG,		\
-		    "map_errcode: minor status=%x",	\
-		    (MINORP) ? *(MINORP) : 0xffffffff)
+    (*(MINORP) = gssint_mecherrmap_map_errcode(*(MINORP)))
+
 #endif /* _KERNEL */
 
 #endif /* _GSS_MECHGLUEP_H */
--- a/usr/src/uts/common/gssapi/mechs/krb5/include/gssapiP_generic.h	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/uts/common/gssapi/mechs/krb5/include/gssapiP_generic.h	Mon Aug 16 17:01:32 2010 -0700
@@ -1,9 +1,6 @@
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
-
-
 /*
  * Copyright 1993 by OpenVision Technologies, Inc.
  * 
@@ -301,4 +298,13 @@
 
 #endif /* 0 */
 
+#ifndef _KERNEL
+int gssint_mecherrmap_init(void);
+void gssint_mecherrmap_destroy(void);
+OM_uint32 gssint_mecherrmap_map(OM_uint32 minor, const gss_OID_desc *oid);
+int gssint_mecherrmap_get(OM_uint32 minor, gss_OID mech_oid,
+			OM_uint32 *mech_minor);
+OM_uint32 gssint_mecherrmap_map_errcode(OM_uint32 errcode);
+#endif
+
 #endif /* _GSSAPIP_GENERIC_H_ */
--- a/usr/src/uts/common/gssapi/mechs/krb5/include/gssapiP_krb5.h	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/uts/common/gssapi/mechs/krb5/include/gssapiP_krb5.h	Mon Aug 16 17:01:32 2010 -0700
@@ -1,9 +1,6 @@
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
-
-
 /*
  * Copyright 2000 by the Massachusetts Institute of Technology.
  * All Rights Reserved.
@@ -843,6 +840,9 @@
                                               gss_buffer_set_t *);
 #endif /* _KERNEL */
 
+OM_uint32 gss_krb5int_initialize_library(void);
+void gss_krb5int_cleanup_library(void);
+
 /* For error message handling.  */
 /* Returns a shared string, not a private copy!  */
 extern char *
@@ -862,11 +862,12 @@
 #define save_error_message krb5_gss_save_error_message
 
 
-#if 0 /* SUNW17PACresync - revisit for full MIT 1.7 resync */
+/* Solaris Kerberos */
+#ifdef _KERNEL
+#define save_error_info(m, ctx)
+#else
 #define save_error_info krb5_gss_save_error_info
 #endif
-#define save_error_info(m, ctx)
-
 
 extern void krb5_gss_delete_error_info(void *p);
 
--- a/usr/src/uts/common/gssapi/mechs/krb5/include/k5-int.h	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/uts/common/gssapi/mechs/krb5/include/k5-int.h	Mon Aug 16 17:01:32 2010 -0700
@@ -1,8 +1,6 @@
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
-
 /*
  * Copyright (C) 1989,1990,1991,1992,1993,1994,1995,2000,2001, 2003,2006 by the Massachusetts Institute of Technology,
  * Cambridge, MA, USA.  All Rights Reserved.
@@ -317,6 +315,10 @@
 					   /* required */
 #define KDC_ERR_SERVER_NOMATCH		26 /* Requested server and */
 					   /* ticket don't match*/
+#define KDC_ERR_MUST_USE_USER2USER      27 /* Server principal valid for */
+					   /*   user2user only */
+#define KDC_ERR_PATH_NOT_ACCEPTED       28 /* KDC policy rejected transited */
+					   /*   path */
 #define KDC_ERR_SVC_UNAVAILABLE		29 /* A service is not
 					    * available that is
 					    * required to process the
@@ -357,7 +359,12 @@
 #define KDC_ERR_CLIENT_NOT_TRUSTED		62 /* client cert not trusted */
 #define KDC_ERR_INVALID_SIG			64 /* client signature verify failed */
 #define KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED	65 /* invalid Diffie-Hellman parameters */
-#define KDC_ERR_CANT_VERIFY_CERTIFICATE		70 /* client cert not verifiable to */
+#define KDC_ERR_CERTIFICATE_MISMATCH            66
+#define KRB_AP_ERR_NO_TGT                       67
+#define KDC_ERR_WRONG_REALM                     68
+#define KRB_AP_ERR_USER_TO_USER_REQUIRED        69
+#define KDC_ERR_CANT_VERIFY_CERTIFICATE         70 /* client cert not verifiable
+ to */
 						   /* trusted root cert */
 #define KDC_ERR_INVALID_CERTIFICATE		71 /* client cert had invalid signature */
 #define KDC_ERR_REVOKED_CERTIFICATE		72 /* client cert was revoked */
@@ -658,6 +665,10 @@
 krb5_error_code krb5_unlock_file (krb5_context, int);
 krb5_error_code krb5_sendto_kdc (krb5_context, const krb5_data *,
 				 const krb5_data *, krb5_data *, int *, int);
+/* Solaris Kerberos */
+krb5_error_code krb5_sendto_kdc2 (krb5_context, const krb5_data *,
+				const krb5_data *, krb5_data *, int *, int,
+				char **);
 
 
 krb5_error_code krb5_get_krbhst (krb5_context, const krb5_data *, char *** );
@@ -2686,12 +2697,54 @@
 extern int krb5int_crypto_init (void);
 extern int krb5int_prng_init(void);
 
+
 /*
  * SUNW14resync
  * Hack (?) to neuter C99 "inline" which causes warnings w/our build.
  */
 #define inline
 
+/* Some data comparison and conversion functions.  */
+#if 0
+static inline int data_cmp(krb5_data d1, krb5_data d2)
+{
+    if (d1.length < d2.length) return -1;
+    if (d1.length > d2.length) return 1;
+    return memcmp(d1.data, d2.data, d1.length);
+}
+static inline int data_eq (krb5_data d1, krb5_data d2)
+{
+    return data_cmp(d1, d2) == 0;
+}
+#else
+static inline int data_eq (krb5_data d1, krb5_data d2)
+{
+    return (d1.length == d2.length
+            && !memcmp(d1.data, d2.data, d1.length));
+}
+#endif
+static inline krb5_data string2data (char *str)
+{
+    krb5_data d;
+    d.magic = KV5M_DATA;
+    d.length = strlen(str);
+    d.data = str;
+    return d;
+}
+/*LINTED*/
+static inline int data_eq_string (krb5_data d, char *s)
+{
+    return data_eq(d, string2data(s));
+}
+/*LINTED*/
+static inline int authdata_eq (krb5_authdata a1, krb5_authdata a2)
+{
+    return (a1.ad_type == a2.ad_type
+            && a1.length == a2.length
+            && !memcmp(a1.contents, a2.contents, a1.length));
+}
+
+
 /* Solaris kerberos */
 krb5_boolean KRB5_CALLCONV is_in_keytype 
 	(krb5_const krb5_enctype *keytype, 
--- a/usr/src/uts/common/gssapi/mechs/krb5/include/k5-thread.h	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/uts/common/gssapi/mechs/krb5/include/k5-thread.h	Mon Aug 16 17:01:32 2010 -0700
@@ -1,6 +1,5 @@
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
@@ -817,6 +816,8 @@
     K5_KEY_COM_ERR,
     K5_KEY_GSS_KRB5_SET_CCACHE_OLD_NAME,
     K5_KEY_GSS_KRB5_CCACHE_NAME,
+    K5_KEY_GSS_KRB5_ERROR_MESSAGE,
+    K5_KEY_GSS_SPNEGO_ERROR_MESSAGE,
     K5_KEY_MAX
 } k5_key_t;
 /* rename shorthand symbols for export */
--- a/usr/src/uts/common/gssapi/mechs/krb5/include/krb5.h	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/uts/common/gssapi/mechs/krb5/include/krb5.h	Mon Aug 16 17:01:32 2010 -0700
@@ -1,6 +1,5 @@
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /* This is the prologue to krb5.h */
@@ -2120,6 +2119,20 @@
 		const krb5_data *,
 		krb5_creds *,
 		krb5_response * );
+
+krb5_error_code krb5_send_tgs2
+	(krb5_context,
+		krb5_flags,
+		const krb5_ticket_times *,
+		const krb5_enctype *,
+		krb5_const_principal,
+		krb5_address * const *,
+		krb5_authdata * const *,
+		krb5_pa_data * const *,
+		const krb5_data *,
+		krb5_creds *,
+	        krb5_response * ,
+		char **);
 #endif
 
 #if KRB5_DEPRECATED
@@ -3003,8 +3016,8 @@
 #define KRB5KDC_ERR_PREAUTH_FAILED               (-1765328360L)
 #define KRB5KDC_ERR_PREAUTH_REQUIRED             (-1765328359L)
 #define KRB5KDC_ERR_SERVER_NOMATCH               (-1765328358L)
-#define KRB5PLACEHOLD_27                         (-1765328357L)
-#define KRB5PLACEHOLD_28                         (-1765328356L)
+#define KRB5KDC_ERR_MUST_USE_USER2USER           (-1765328357L)
+#define KRB5KDC_ERR_PATH_NOT_ACCEPTED            (-1765328356L)
 #define KRB5KDC_ERR_SVC_UNAVAILABLE              (-1765328355L)
 #define KRB5PLACEHOLD_30                         (-1765328354L)
 #define KRB5KRB_AP_ERR_BAD_INTEGRITY             (-1765328353L)
@@ -3043,9 +3056,9 @@
 #define KRB5KDC_ERR_INVALID_SIG                  (-1765328320L)
 #define KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED (-1765328319L)
 #define KRB5KDC_ERR_CERTIFICATE_MISMATCH         (-1765328318L)
-#define KRB5PLACEHOLD_67                         (-1765328317L)
-#define KRB5PLACEHOLD_68                         (-1765328316L)
-#define KRB5PLACEHOLD_69                         (-1765328315L)
+#define KRB5KRB_AP_ERR_NO_TGT                    (-1765328317L)
+#define KRB5KDC_ERR_WRONG_REALM                  (-1765328316L)
+#define KRB5KRB_AP_ERR_USER_TO_USER_REQUIRED     (-1765328315L)
 #define KRB5KDC_ERR_CANT_VERIFY_CERTIFICATE      (-1765328314L)
 #define KRB5KDC_ERR_INVALID_CERTIFICATE          (-1765328313L)
 #define KRB5KDC_ERR_REVOKED_CERTIFICATE          (-1765328312L)
--- a/usr/src/uts/common/gssapi/mechs/krb5/mech/delete_sec_context.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/uts/common/gssapi/mechs/krb5/mech/delete_sec_context.c	Mon Aug 16 17:01:32 2010 -0700
@@ -1,6 +1,5 @@
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 /*
  * Copyright 1993 by OpenVision Technologies, Inc.
@@ -153,8 +152,10 @@
 
       if ((major = kg_seal(minor_status, *context_handle, 0,
 			   GSS_C_QOP_DEFAULT,
-			   &empty, NULL, output_token, KG_TOK_DEL_CTX)))
+			   &empty, NULL, output_token, KG_TOK_DEL_CTX))) {
+	 save_error_info(*minor_status, context);
 	 return(major);
+      }
    }
 
    /* invalidate the context handle */
--- a/usr/src/uts/common/gssapi/mechs/krb5/mech/gssapi_krb5.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/uts/common/gssapi/mechs/krb5/mech/gssapi_krb5.c	Mon Aug 16 17:01:32 2010 -0700
@@ -1,6 +1,5 @@
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 
@@ -60,6 +59,9 @@
 /* For declaration of krb5_ser_context_init */
 #include "k5-int.h"
 #include "gssapiP_krb5.h"
+#ifndef	_KERNEL
+#include "gss_libinit.h"
+#endif
 
 /*
  * Solaris Kerberos
@@ -245,10 +247,12 @@
 	    if (name) {
 		name = strdup(name);
 		if (name == NULL)
-		    err = errno;
+		    err = ENOMEM;
 	    }
 	}
-	if (context)
+	if (err && context)
+	    save_error_info(err, context);
+   	if (context)
 	    krb5_free_context(context);
     }
 
@@ -375,4 +379,21 @@
     return GSS_S_UNAVAILABLE;
 }
 
+
+#if 0 /* Solaris Kerberos - revisit for full 1.7/next resync */
+MAKE_INIT_FUNCTION(gss_krb5int_lib_init);
+MAKE_FINI_FUNCTION(gss_krb5int_lib_fini);
 #endif
+
+OM_uint32 gss_krb5int_initialize_library (void)
+{
+#if 0 /* Solaris Kerberos - revisit for full 1.7/next resync */
+#ifdef _GSS_STATIC_LINK
+	return gssint_mechglue_initialize_library();
+#else
+	return CALL_INIT_FUNCTION(gss_krb5int_lib_init);
+#endif
+#endif
+	return gssint_initialize_library();
+}
+#endif /* !KERNEL */
--- a/usr/src/uts/common/gssapi/mechs/krb5/mech/import_sec_context.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/uts/common/gssapi/mechs/krb5/mech/import_sec_context.c	Mon Aug 16 17:01:32 2010 -0700
@@ -1,9 +1,6 @@
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
-
-
 /*
  * lib/gssapi/krb5/import_sec_context.c
  *
@@ -107,8 +104,9 @@
 
     kret = krb5_gss_ser_init(context);
     if (kret) {
+	*minor_status = kret;
+	save_error_info(*minor_status, context);
 	krb5_free_context(context);
-	*minor_status = kret;
 	return GSS_S_FAILURE;
     }
 
@@ -131,8 +129,9 @@
      * and it will get freed by delete_sec_context.
      */
     if (kret) {
+       *minor_status = (OM_uint32) kret;
+       save_error_info(*minor_status, context);
        krb5_free_context(context);
-       *minor_status = (OM_uint32) kret;
        return(GSS_S_FAILURE);
     }
 
--- a/usr/src/uts/common/gssapi/mechs/krb5/mech/k5seal.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/uts/common/gssapi/mechs/krb5/mech/k5seal.c	Mon Aug 16 17:01:32 2010 -0700
@@ -1,9 +1,6 @@
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
-
-
 /*
  * Copyright 1993 by OpenVision Technologies, Inc.
  *
@@ -419,6 +416,7 @@
     context = ctx->k5_context;
     if ((code = krb5_timeofday(context, &now))) {
 	*minor_status = code;
+        save_error_info(*minor_status, context);
 	KRB5_LOG(KRB5_ERR, "kg_seal() end, krb5_timeofday() error code=%d\n", code);
 	return (GSS_S_FAILURE);
     }
@@ -446,6 +444,7 @@
 
     if (code) {
 	*minor_status = code;
+        save_error_info(*minor_status, context);
 	KRB5_LOG(KRB5_ERR, "kg_seal() end, make_seal_token_v1() "
 		"error code=%d\n", code);
 	return (GSS_S_FAILURE);
--- a/usr/src/uts/common/gssapi/mechs/krb5/mech/k5sealv3.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/uts/common/gssapi/mechs/krb5/mech/k5sealv3.c	Mon Aug 16 17:01:32 2010 -0700
@@ -1,9 +1,6 @@
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
-
-
 /*
  * lib/gssapi/krb5/k5sealv3.c
  *
@@ -566,6 +563,7 @@
 	error:
 	    FREE(plain.data, plain.length);
 	    *minor_status = err;
+	    save_error_info(*minor_status, context);
 	    return GSS_S_BAD_SIG; /* XXX */
 	}
 	FREE(plain.data, plain.length);
--- a/usr/src/uts/common/gssapi/mechs/krb5/mech/k5unseal.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/uts/common/gssapi/mechs/krb5/mech/k5unseal.c	Mon Aug 16 17:01:32 2010 -0700
@@ -1,9 +1,6 @@
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  */
-
-
 /*
  * Copyright 2001 by the Massachusetts Institute of Technology.
  * Copyright 1993 by OpenVision Technologies, Inc.
@@ -729,6 +726,11 @@
 
     *minor_status = err;
 
+#ifndef _KERNEL
+    if (err != 0)
+	save_error_info (*minor_status, ctx->k5_context);
+#endif
+
     KRB5_LOG(KRB5_INFO, "kg_unseal() end, err = %d", err);
 
     return(err);
--- a/usr/src/uts/common/gssapi/mechs/krb5/mech/val_cred.c	Mon Aug 16 16:49:45 2010 -0700
+++ b/usr/src/uts/common/gssapi/mechs/krb5/mech/val_cred.c	Mon Aug 16 17:01:32 2010 -0700
@@ -1,5 +1,6 @@
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
+/*
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
+ */
 /*
  * Copyright 1997 by Massachusetts Institute of Technology
  * All Rights Reserved.
@@ -90,7 +91,8 @@
 	krb5_gss_cred_id_t cred = (krb5_gss_cred_id_t) cred_handle;
 	k5_mutex_assert_locked(&cred->lock);
 	k5_mutex_unlock(&cred->lock);
-    }
+    } else /* Solaris Kerberos - added this else */
+        save_error_info(*minor_status, context);
     krb5_free_context(context);
     return maj;
 }