Re: aname_to_locaname vs gssapi svc/host.domain.org@REALM

Harry Coin <[email protected]>
Newsgroups gmane.comp.encryption.kerberos.heimdal.general
Message-ID <[email protected]>
On 10/24/2011 5:17 PM, Nico Williams wrote:
> On Mon, Oct 24, 2011 at 4:50 PM, Harry Coin<[email protected]>  wrote:
>> On 10/24/2011 4:19 PM, Nico Williams wrote:
>>
>>> But again, *daemons* have NO business using NFS resources unless the
>>> host is a diskless NFS client.  But as I've explained, NFS is a very
>>> poor protocol for diskless clients.  If you do not use NFS for
>>> disklessness, then that problem goes away.
>> I must respectfully disagree.   While few that might come prepackaged as
>> system utilities meant to deliver services to the masses may not, to say
>> that none have any business doing so denies the ongoing use of user-specific
>> enterprise wide efforts that do just that.
> Sure, Apache comes to mind as a daemon that needs to go all over (per
> configuration).  But sldapd?  nscd?  no, they don't need to, not
> outside a diskless case.  And Apache is special because of what it's
> intended for.
>
> Let's get a few things clear:
>
>   - System daemons have no business being NFS clients outside diskless clients.
>   - Daemons like Apache, or services that *you* write, might.
>       - Such daemons can access NFS using whatever GSS credentials they
> care to: all they have to do is have acquired them (e.g., have
> kinit'ed).  I believe this is true of Linux's client as well as
> Solaris'.
>   - On the server-side you'll need to make authorization decisions.
> These can be done using PACs (if you have them in your tickets and
> have the ability to use them) and/or principal name->authorization
> context mappings.
>      - Typical NFS servers let you map arbitrary principals to
> arbitrary "user accounts" as a way of mapping client principals to
> authorization contexts.  I believe this is true of Linux's as well as
> Solaris' servers.
>      - Yes, Solaris lets you use aname2lname() for this, but that's not
> the only option it gives you.  You can also list principal->username
> mappings in a "gsscred" file (man gsscred).
>
> Now, can you clarify what it is you want?
>
>

Sure.  I want everything that worked great here to continue as before 
(ldap, pam_krb5, nslcd, local software) -- except with -sec=krb5x 
kerberos benefits as advertised.  Specifically

A)  without taboo against uid:uname:principal name combinations if I 
specifically indicate I permit them.

and, as pre-kerberos,

B) if a server should offer  N directories for remote access, but 
restricting each of them specifically to F(N) boxes, where F(N) < all 
the boxen in the realm, there exists at least one directory available to 
processes on  box Y but not Z, and likely too some on Z not available to 
Y.  then:

Files written on the share by account-name:uid QQ on Y  will not be 
available to QQ if on Z.  Such files will appear with the QQ's correct 
uname:uid.  Should QQ write on Z, such files may not be visible if on Y, 
but will appear correctly named on Z.   If a process running as QQ 
exists on the server it will have access to both the files written from 
Y and from Z by user QQ.

C)   Unames map onto uids in a 1 to 1 way.   Unames map onto 
'multi-application-use' kerberos principals in a 1 to 1 way.

Just as they all did before kerberos meets network wire 
integrity/privacy assurance.

Except, now with the advertised kerberos ability to authenticate the 
user, or to provide integrity checks of the traffic, or to encrypt the 
trafffic entirely if asked.

Being that it's all free instead of asking for a refund I wrote some 
patches that get it done.

Harry

I did this against freebsd 8stable it's all good:

patch -p
diff -u /usr/src/crypto/heimdal/lib/krb5/aname_to_localname.c 
new/aname_to_localname.c
--- /usr/src/crypto/heimdal/lib/krb5/aname_to_localname.c    2008-05-07 
08:39:37.000000000 -0500
+++ new/aname_to_localname.c    2011-10-21 15:10:50.000000000 -0500
@@ -32,9 +32,56 @@
   */

  #include <krb5_locl.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <pwd.h>

  RCSID("$Id: aname_to_localname.c 13863 2004-05-25 21:46:46Z lha $");

+static int reverse_etck5logind(krb5_context 
context,krb5_const_principal principal,char **user ){
+    DIR *d;
+    struct dirent *dent;
+    struct passwd *pwd;
+    krb5_boolean result;
+    int errc;
+#ifdef POSIX_GETPWNAM_R
+    char pwbuf[2048];
+    struct passwd pw;
+#endif
+    if (user==NULL) return EFAULT;
+    *user=NULL;
+    if((d = opendir(ETCK5LOGIND)) == NULL)
+    return errno;
+    setpassent(TRUE);
+    while((dent = readdir(d)) != NULL) {
+    char *fname;
+    if(strcmp(dent->d_name, ".") == 0 ||
+       strcmp(dent->d_name, "..") == 0 ||
+       dent->d_name[0] == '#' ||              /* emacs autosave */
+       dent->d_name[strlen(dent->d_name) - 1] == '~') /* emacs backup */
+        continue;
+#ifdef POSIX_GETPWNAM_R
+    if(getpwnam_r(dent->d_name, &pw, pwbuf, sizeof(pwbuf), &pwd) != 0) 
continue;
+#else
+        pwd = getpwnam (dent->d_name);
+#endif
+    if (pwd == NULL) continue;
+    asprintf(&fname,"%s%s",ETCK5LOGIND,dent->d_name);
+    errc = 
krb5_internal_check_one_file(context,fname,pwd,principal,2,&result);
+    free(fname);
+    if (0==errc) {
+        if (result) {
+        *user = strdup(dent->d_name);
+        break;
+        }
+    }
+    }
+    endpwent();
+    closedir(d);
+    return 0;
+}
+
+
  krb5_error_code KRB5_LIB_FUNCTION
  krb5_aname_to_localname (krb5_context context,
               krb5_const_principal aname,
@@ -46,7 +93,9 @@
      int valid;
      size_t len;
      const char *res;
-
+    char *rk5;
+
+    //Fastest: one part principal name in the default realm.
      ret = krb5_get_default_realms (context, &lrealms);
      if (ret)
      return ret;
@@ -59,13 +108,31 @@
      }
      }
      krb5_free_host_realm (context, lrealms);
-    if (valid == 0)
-    return KRB5_NO_LOCALNAME;

-    if (aname->name.name_string.len == 1)
+    if (valid && (aname->name.name_string.len == 1)) {
      res = aname->name.name_string.val[0];
-    else if (aname->name.name_string.len == 2
- && strcmp (aname->name.name_string.val[1], "root") == 0) {
+        len = strlen (res);
+    if (len >= lnsize)
+        return ERANGE;
+    strlcpy (lname, res, lnsize);
+    return 0; // do not clutter the cache by adding these 
simplename@REALM principals.
+    }
+
+    //Second fastest: check the cache.
+    rk5=NULL;
+    _krb5_luser_principal_cache_query_princ(context,aname,&rk5); 
//check the cache.
+    if (rk5!=NULL) { //found it.
+        len = strlen (res);
+    if (len >= lnsize)
+        return ERANGE;
+    strlcpy (lname, rk5, lnsize);
+    free(rk5);
+    return 0;
+    }
+
+    //Third fastest: check the whatnot/root@REALM format.
+    if ((valid == 1) && (aname->name.name_string.len == 2)
+ && (strcmp (aname->name.name_string.val[1], "root") == 0)) {
      krb5_principal rootprinc;
      krb5_boolean userok;

@@ -75,18 +142,27 @@
      if (ret)
          return ret;

-    userok = krb5_kuserok(context, rootprinc, res);
+    userok = krb5_kuserok(context, rootprinc, res); //note this makes 
use of the cache, so it is fast.
      krb5_free_principal(context, rootprinc);
-    if (!userok)
-        return KRB5_NO_LOCALNAME;
+    if (userok) {
+        len = strlen (res);
+        if (len >= lnsize)
+        return ERANGE;
+        strlcpy (lname, res, lnsize);
+        return 0;
+    }
+    }

-    } else
-    return KRB5_NO_LOCALNAME;
+    //Slowest: check the files and if found add the result to the cache 
before returning it.
+    reverse_etck5logind(context,aname,&rk5);
+    if (rk5!=NULL) {
+        len = strlen (rk5);
+        if (len >= lnsize) { free(rk5); return ERANGE; }
+        strlcpy (lname, rk5, lnsize);
+        free(rk5);
+        return 0;
+    }
+    return KRB5_NO_LOCALNAME;
+}

-    len = strlen (res);
-    if (len >= lnsize)
-    return ERANGE;
-    strlcpy (lname, res, lnsize);

-    return 0;
-}
diff -u /usr/src/crypto/heimdal/lib/krb5/context.c new/context.c
--- /usr/src/crypto/heimdal/lib/krb5/context.c    2008-05-07 
08:39:38.000000000 -0500
+++ new/context.c    2011-10-21 15:19:52.000000000 -0500
@@ -230,6 +230,16 @@
      }
      HEIMDAL_MUTEX_init(p->mutex);

+    p->luser_principal_cache_mutex = malloc(sizeof(HEIMDAL_MUTEX));
+    if (p->luser_principal_cache_mutex == NULL) {
+    free(p->mutex);
+    free(p);
+    return ENOMEM;
+    }
+    HEIMDAL_MUTEX_init(p->luser_principal_cache_mutex);
+
+    p->luser_principal_cache=NULL;
+
      ret = krb5_get_default_config_files(&files);
      if(ret)
      goto out;
@@ -252,6 +262,7 @@

      p->num_kt_types = 0;
      p->kt_types     = NULL;
+    krb5_kt_register (p, &krb5_fkt_uid_ops);
      krb5_kt_register (p, &krb5_fkt_ops);
      krb5_kt_register (p, &krb5_wrfkt_ops);
      krb5_kt_register (p, &krb5_javakt_ops);
@@ -281,6 +292,7 @@
  void KRB5_LIB_FUNCTION
  krb5_free_context(krb5_context context)
  {
+    krb5_luser_principal_cache_free(context);
      if (context->default_cc_name)
      free(context->default_cc_name);
      if (context->default_cc_name_env)
@@ -302,6 +314,10 @@
      HEIMDAL_MUTEX_destroy(context->mutex);
      free(context->mutex);
      }
+    if (context->luser_principal_cache_mutex != NULL) {
+    HEIMDAL_MUTEX_destroy(context->luser_principal_cache_mutex);
+    free(context->luser_principal_cache_mutex);
+    }
      memset(context, 0, sizeof(*context));
      free(context);
  }
diff -u /usr/src/crypto/heimdal/lib/krb5/keytab_file.c new/keytab_file.c
--- /usr/src/crypto/heimdal/lib/krb5/keytab_file.c    2008-05-07 
08:39:37.000000000 -0500
+++ new/keytab_file.c    2011-10-21 15:11:05.000000000 -0500
@@ -268,6 +268,42 @@
  }

  static krb5_error_code
+fkt_uid_resolve(krb5_context context, const char *name, krb5_keytab id)
+{
+    struct fkt_data *d;
+    char *c, *b;
+
+    d = malloc(sizeof(*d));
+    if(d == NULL) {
+    krb5_set_error_string (context, "malloc: out of memory");
+    return ENOMEM;
+    }
+    c = strdup(name);
+    if (c==NULL) return ENOMEM;
+    b = index(c,'.');
+    if (b!=NULL) {
+        *b=0;
+        if (asprintf(&d->filename,"%s.%d.%s",c,geteuid(),b+1)<2) return 
ENOMEM;
+        if (access(d->filename,R_OK)) {
+            free(d->filename);
+            *b='.';
+            d->filename = c;
+        } else free(c);
+    } else {
+        d->filename = c;
+    }
+    if(d->filename == NULL) {
+    free(d);
+    krb5_set_error_string (context, "malloc: out of memory");
+    return ENOMEM;
+    }
+    d->flags = 0;
+    id->data = d;
+    return 0;
+}
+
+
+static krb5_error_code
  fkt_resolve_java14(krb5_context context, const char *name, krb5_keytab id)
  {
      krb5_error_code ret;
@@ -656,6 +692,19 @@
      return 0;
  }

+const krb5_kt_ops krb5_fkt_uid_ops = {
+    "UIDF",
+    fkt_uid_resolve,
+    fkt_get_name,
+    fkt_close,
+    NULL, /* get */
+    fkt_start_seq_get,
+    fkt_next_entry,
+    fkt_end_seq_get,
+    fkt_add_entry,
+    fkt_remove_entry
+};
+
  const krb5_kt_ops krb5_fkt_ops = {
      "FILE",
      fkt_resolve,
diff -u /usr/src/crypto/heimdal/lib/krb5/krb5-private.h new/krb5-private.h
--- /usr/src/crypto/heimdal/lib/krb5/krb5-private.h    2008-05-07 
08:39:40.000000000 -0500
+++ new/krb5-private.h    2011-10-21 15:11:14.000000000 -0500
@@ -444,4 +444,31 @@
      krb5_context /*context*/,
      int /*fd*/);

+
+void
+_krb5_luser_principal_add_to_cache(
+    krb5_context /*context*/,
+    krb5_const_principal /*princ*/,
+    const char */*luser*/);
+
+krb5_error_code
+_krb5_luser_principal_cache_query_luser(
+    krb5_context /*context*/,
+    const char */*luser*/,
+    krb5_principal */*princ*/);
+
+krb5_error_code
+_krb5_luser_principal_cache_query_princ(
+    krb5_context /*context*/,
+    krb5_const_principal /*princ*/,
+    char **/*luser*/);
+
+krb5_error_code
+krb5_internal_check_one_file(krb5_context /*context*/,
+           const char */*filename*/,
+           struct passwd */*pwd*/,
+           krb5_const_principal /*principal*/,
+           int /*pattern_match_level*/,
+           krb5_boolean */*result*/);
+
  #endif /* __krb5_private_h__ */
diff -u /usr/src/crypto/heimdal/lib/krb5/krb5-protos.h new/krb5-protos.h
--- /usr/src/crypto/heimdal/lib/krb5/krb5-protos.h    2008-05-07 
08:39:38.000000000 -0500
+++ new/krb5-protos.h    2011-10-21 15:11:21.000000000 -0500
@@ -4107,6 +4107,10 @@
  krb5_error_code KRB5_LIB_FUNCTION
  krb5_xfree (void */*ptr*/);

+void KRB5_LIB_FUNCTION
+krb5_luser_principal_cache_free(
+    krb5_context context/*context*/);
+
  #ifdef __cplusplus
  }
  #endif
diff -u /usr/src/crypto/heimdal/lib/krb5/krb5.h new/krb5.h
--- /usr/src/crypto/heimdal/lib/krb5/krb5.h    2008-05-07 
08:39:37.000000000 -0500
+++ new/krb5.h    2011-10-21 15:11:27.000000000 -0500
@@ -686,6 +686,7 @@
  extern const krb5_cc_ops krb5_mcc_ops;
  extern const krb5_cc_ops krb5_kcm_ops;

+extern const krb5_kt_ops krb5_fkt_uid_ops;
  extern const krb5_kt_ops krb5_fkt_ops;
  extern const krb5_kt_ops krb5_wrfkt_ops;
  extern const krb5_kt_ops krb5_javakt_ops;
diff -u /usr/src/crypto/heimdal/lib/krb5/krb5_locl.h new/krb5_locl.h
--- /usr/src/crypto/heimdal/lib/krb5/krb5_locl.h    2009-08-03 
03:13:06.000000000 -0500
+++ new/krb5_locl.h    2011-10-21 15:11:32.000000000 -0500
@@ -167,6 +167,9 @@
  /* should this be public? */
  #define KEYTAB_DEFAULT "ANY:FILE:" SYSCONFDIR "/krb5.keytab,krb4:" 
SYSCONFDIR "/srvtab"
  #define KEYTAB_DEFAULT_MODIFY "FILE:" SYSCONFDIR "/krb5.keytab"
+#define ETCK5LOGIND "/etc/k5login.d/"
+
+

  #define MODULI_FILE SYSCONFDIR "/krb5.moduli"

@@ -198,6 +201,8 @@
  #define KRB5_INIT_CREDS_NO_C_CANON_CHECK    2
  };

+struct _krb5_luser_principal_cache; //defined&used only in misc.c
+
  typedef struct krb5_context_data {
      krb5_enctype *etypes;
      krb5_enctype *etypes_des;
@@ -239,6 +244,10 @@
  #define KRB5_CTX_F_DNS_CANONICALIZE_HOSTNAME    1
  #define KRB5_CTX_F_CHECK_PAC            2
      struct send_to_kdc *send_to_kdc;
+
+    void *luser_principal_cache_mutex;
+    struct _krb5_luser_principal_cache *luser_principal_cache;
+
  } krb5_context_data;

  #define KRB5_DEFAULT_CCNAME_FILE "FILE:/tmp/krb5cc_%{uid}"
diff -u /usr/src/crypto/heimdal/lib/krb5/kuserok.c new/kuserok.c
--- /usr/src/crypto/heimdal/lib/krb5/kuserok.c    2008-05-07 
08:39:38.000000000 -0500
+++ new/kuserok.c    2011-10-21 15:11:39.000000000 -0500
@@ -39,11 +39,12 @@
  /* see if principal is mentioned in the filename access file, return
     TRUE (in result) if so, FALSE otherwise */

-static krb5_error_code
-check_one_file(krb5_context context,
+krb5_error_code
+krb5_internal_check_one_file(krb5_context context,
             const char *filename,
             struct passwd *pwd,
-           krb5_principal principal,
+           krb5_const_principal principal,
+           int pattern_match_level,
             krb5_boolean *result)
  {
      FILE *f;
@@ -90,10 +91,14 @@
          }
      }
      *newline = '\0';
+    if (pattern_match_level>1) {
+        if (!strncmp(buf,"#STOP",5)) break;
+    }
      ret = krb5_parse_name (context, buf, &tmp);
      if (ret)
          continue;
-    *result = krb5_principal_compare (context, principal, tmp);
+    *result = (pattern_match_level ? krb5_principal_match (context, 
principal, tmp): krb5_principal_compare (context, principal, tmp));
+    if (*result) 
_krb5_luser_principal_add_to_cache(context,principal,pwd->pw_name);
      krb5_free_principal (context, tmp);
      if (*result) {
          fclose (f);
@@ -157,7 +162,7 @@
         dent->d_name[strlen(dent->d_name) - 1] == '~') /* emacs backup */
          continue;
      snprintf(filename, sizeof(filename), "%s/%s", dirname, dent->d_name);
-    ret = check_one_file(context, filename, pwd, principal, result);
+    ret = krb5_internal_check_one_file(context, filename, pwd, 
principal, FALSE, result);
      if(ret == 0 && *result == TRUE)
          break;
      ret = 0; /* don't propagate errors upstream */
@@ -194,6 +199,7 @@
      }
      }
      krb5_free_host_realm (context, realms);
+    if (result) 
_krb5_luser_principal_add_to_cache(context,principal,luser);
      return result;
  }

@@ -226,6 +232,18 @@
      if (pwd == NULL)
      return FALSE;

+    
if(0==_krb5_luser_principal_cache_query_luser(context,luser,&principal)) 
return TRUE;
+
+    buflen = strlen(luser) + strlen(ETCK5LOGIND) + 1;
+    buf = malloc(buflen);
+    if(buf == NULL)
+    return FALSE;
+    strlcpy(buf,ETCK5LOGIND,buflen);
+    strlcat(buf,luser,buflen);
+    ret = krb5_internal_check_one_file(context, buf, pwd, principal, 
TRUE, &result);
+    free(buf);
+    if (ret == 0) return result;  //if there is a file, don't process 
further.
+
  #define KLOGIN "/.k5login"
      buflen = strlen(pwd->pw_dir) + sizeof(KLOGIN) + 2; /* 2 for .d */
      buf = malloc(buflen);
@@ -234,7 +252,7 @@
      /* check user's ~/.k5login */
      strlcpy(buf, pwd->pw_dir, buflen);
      strlcat(buf, KLOGIN, buflen);
-    ret = check_one_file(context, buf, pwd, principal, &result);
+    ret = krb5_internal_check_one_file(context, buf, pwd, principal, 
FALSE, &result);

      if(ret == 0 && result == TRUE) {
      free(buf);
diff -u /usr/src/crypto/heimdal/lib/krb5/misc.c new/misc.c
--- /usr/src/crypto/heimdal/lib/krb5/misc.c    2008-05-07 
08:39:37.000000000 -0500
+++ new/misc.c    2011-10-21 15:29:06.000000000 -0500
@@ -84,3 +84,205 @@
      krb5_clear_error_string(context);
      return ret;
  }
+
+
+//Begin caching routines used by kuserok and aname_to_lname.  Here we 
hope to save results of previous scans of
+//.k5login and similar files to speed repeated scans.
+
+//Note these routines impose no significant memory overhead until 
kuserok or aname_to_lname first returns a positive result.
+
+//ADMINISTRATIVE EFFECT:  kuserok and aname_to_lname almost certainly 
will approve associations among deleted users/principals
+//for 5 minutes after their removal from .k5login/ local passwd / 
realm, or until the library is reloaded, or until
+//krb5_flush_luser_principal_cache(context) is called.
+
+//Provided are four major entry points:
+//add new association validating a link between a principal name and a 
user name,
+//lookup principal name given local user name, lookup user name given 
principal name,
+//and free internal structures.
+//Entries are saved for 5 minutes. In the interests of overall speed, 
it is possible
+//but not very likely that an association added within the past 5 
minutes will not be found in the cache, depending
+//on mostly on how many new associations were recently added and 
whether the desired is among the then oldest.
+
+struct _krb5_luser_principal_cache_entry { //the cache is made up as an 
array of these elements.
+    char *luser; //local user name, null terminated string.
+    krb5_principal princ;
+    clock_t expiration_ticks;  //if clock()>expiration ticks, 
ignore&delete entry
+    unsigned int lhe,phe; //index into luser,principal hash tables 
referring to this entry offset+1.
+};
+#define KRB5_LUSER_PRINCIPAL_CACHE_ARRAYSIZE 1023
+#define KRB5_LUSER_PRINCIPAL_CACHE_FANOUT 3
+#define KRB5_LUSER_PRINCIPAL_CACHE_HASHSIZE 
(KRB5_LUSER_PRINCIPAL_CACHE_FANOUT*KRB5_LUSER_PRINCIPAL_CACHE_ARRAYSIZE)
+
+struct _krb5_luser_principal_cache {
+    struct _krb5_luser_principal_cache_entry 
cache[KRB5_LUSER_PRINCIPAL_CACHE_ARRAYSIZE];
+    int luser_hash_table[KRB5_LUSER_PRINCIPAL_CACHE_HASHSIZE];     
//luser_hash_table[x]==0->free,
+                                       //else 
cache[luser_hash_table[x]-1].lhe==x
+    int principal_hash_table[KRB5_LUSER_PRINCIPAL_CACHE_HASHSIZE]; 
//principal_hash_table[x]==0->free,
+                                       //else 
cache[principal_hash_table[x]-1].phe==x
+};
+
+
+
+static unsigned int char_hash(const char *s){
+    unsigned int h;
+    if (s==NULL) return 0;
+    for(h=0;*s;s++) { h = (h&0x80000000) ? (1|(h<<1)) : h<<1; h = h ^ *s; }
+    return h;
+}
+
+static unsigned int luser_hash(const char *s){
+    return char_hash(s)%KRB5_LUSER_PRINCIPAL_CACHE_HASHSIZE;
+}
+
+static unsigned int princ_hash(krb5_const_principal princ){
+    unsigned int h; int i;
+    if (princ==NULL) return 0;
+    for(h=0,i=0;i<princ->name.name_string.len;i++) h ^= 
char_hash(princ->name.name_string.val[i]);
+    h ^= char_hash(princ->realm);
+    return  h % KRB5_LUSER_PRINCIPAL_CACHE_HASHSIZE;
+}
+
+static void free_pce(krb5_context context,struct 
_krb5_luser_principal_cache_entry *pce){
+    if (pce==NULL) return;
+    free(pce->luser); pce->luser=NULL;
+    krb5_free_principal(context,pce->princ); pce->princ=NULL;
+    pce->expiration_ticks=0;
+    context->luser_principal_cache->luser_hash_table[pce->lhe]=0;
+    context->luser_principal_cache->principal_hash_table[pce->phe]=0;
+}
+
+static int choose_pce(krb5_context context,int *base,unsigned int h){
+    clock_t oldest,pce_expire;
+    unsigned int oldest_h;  int i;
+    struct _krb5_luser_principal_cache_entry *cache;
+    oldest=clock();
+    cache = &context->luser_principal_cache->cache[0];
+    
for(i=0,oldest_h=h;i<KRB5_LUSER_PRINCIPAL_CACHE_FANOUT;i++,h=(h+1)%KRB5_LUSER_PRINCIPAL_CACHE_HASHSIZE){
+    if (base[h]==0) return h;
+    pce_expire = cache[base[h]-1].expiration_ticks;
+    if (pce_expire<oldest) { oldest = pce_expire; oldest_h=h;}
+    }
+    return oldest_h;
+}
+
+
+
+void  _krb5_luser_principal_add_to_cache(krb5_context 
context,krb5_const_principal princ,const char *luser){
+    //Here we record that one of the k5login mechanisms has validated 
an association between a principal and a local user.
+    //If either the user or principal is given to a lookup routine up 
to 5 minutes later, the other might be found quickly.
+    //Note 'might'. Expected associations might not be found and as 
this exists for speed only, misses only cost time as the
+    //fallback is to search the primary sources.
+    //NB: Assumed is that a lookup of what we're inserting failed. It's 
no error for something to be inserted twice, just wasteful.
+    unsigned int h,old_h; int i;
+    clock_t now,old;
+    struct _krb5_luser_principal_cache *pc;
+    struct _krb5_luser_principal_cache_entry *pce;
+    if ((luser==NULL) || (princ==NULL) || (context==NULL)) return;
+    HEIMDAL_MUTEX_lock(&context->luser_principal_cache_mutex);
+    if (context->luser_principal_cache==NULL) { //if we haven't done a 
kuser_ok or aname_to_lname yet, reserve the cache resources.
+    context->luser_principal_cache = (struct 
_krb5_luser_principal_cache *) 
calloc(1,sizeof(*context->luser_principal_cache));
+    sranddev();
+    }
+    if (context->luser_principal_cache==NULL) goto bail;
+    //Find any free entry, or absent that one older than 30 minutes, or 
absent that the oldest one.
+    pc = context->luser_principal_cache;
+    old = now = clock();
+    old_h = h = rand() % KRB5_LUSER_PRINCIPAL_CACHE_ARRAYSIZE;
+    
for(i=0,pce=&pc->cache[h];i<KRB5_LUSER_PRINCIPAL_CACHE_ARRAYSIZE;i++,h=(h+1) 
% KRB5_LUSER_PRINCIPAL_CACHE_ARRAYSIZE,pce=&pc->cache[h]){
+        if (pce->luser==NULL) break;
+        if (pce->expiration_ticks<now) break;
+    if (pce->expiration_ticks<old) { old=pce->expiration_ticks; old_h=h; }
+    }
+    if(i==KRB5_LUSER_PRINCIPAL_CACHE_ARRAYSIZE) { pce= 
&pc->cache[old_h]; h = old_h; } //expire oldest
+    if (pce!=NULL) free_pce(context,pce);
+    pce->luser = strdup(luser);
+    krb5_copy_principal(context,princ,&pce->princ);
+    pce->expiration_ticks = clock()*5*60*CLOCKS_PER_SEC;
+    //Entry secure, now locate it in the hash table.
+    pce->lhe = 
choose_pce(context,&pc->luser_hash_table[0],luser_hash(pce->luser));
+    if (pc->luser_hash_table[pce->lhe]!=0) 
free_pce(context,&pc->cache[pc->luser_hash_table[pce->lhe]-1]);
+    pce->phe = 
choose_pce(context,&pc->principal_hash_table[0],princ_hash(pce->princ));
+    if (pc->principal_hash_table[pce->phe]!=0) 
free_pce(context,&pc->cache[pc->principal_hash_table[pce->phe]-1]);
+    pc->luser_hash_table[pce->lhe]=h+1;
+    pc->principal_hash_table[pce->phe]=h+1;
+    //{ char *ppn; krb5_unparse_name(context,princ,&ppn); 
printf("Cached user %s == principal %s - 
%d/%d/%d\n",luser,ppn,h,pce->lhe,pce->phe); free(ppn); }
+bail:
+    HEIMDAL_MUTEX_unlock(&context->luser_principal_cache_mutex);
+}
+
+void KRB5_LIB_FUNCTION krb5_luser_principal_cache_free(krb5_context 
context) {
+    int i;
+    struct _krb5_luser_principal_cache_entry *ce;
+    HEIMDAL_MUTEX_lock(&context->luser_principal_cache_mutex);
+    if (context->luser_principal_cache!=NULL) {
+    
for(i=0,ce=&context->luser_principal_cache->cache[0];i<KRB5_LUSER_PRINCIPAL_CACHE_ARRAYSIZE;i++,ce++) 
free_pce(context,ce);
+    free(context->luser_principal_cache);
+        context->luser_principal_cache=NULL;
+    }
+    HEIMDAL_MUTEX_unlock(&context->luser_principal_cache_mutex);
+}
+
+//if *princ!=NULL, return 0 only if the luser<->princ match is in the 
cache, else ENOENT.
+//if *princ==NULL, if luser is in the cache, set princ to the first hit 
and return 0, else ENOENT
+krb5_error_code  _krb5_luser_principal_cache_query_luser(krb5_context 
context,const char *luser,krb5_principal *princ) {
+    int ret,i,j; unsigned int h;
+    struct _krb5_luser_principal_cache *pc;
+    struct _krb5_luser_principal_cache_entry *pce;
+    clock_t now;
+    pc = context->luser_principal_cache;
+    if ((pc==NULL) || (princ==NULL) || (context==NULL) || 
(luser==NULL)) return ENOENT;
+    ret = 0;
+    h = luser_hash(luser);
+    now = clock();
+    HEIMDAL_MUTEX_lock(&context->luser_principal_cache_mutex);
+    
for(i=0;i<KRB5_LUSER_PRINCIPAL_CACHE_FANOUT;i++,h=(h+1)%KRB5_LUSER_PRINCIPAL_CACHE_HASHSIZE){
+    j=pc->luser_hash_table[h];
+    if (j!=0) {
+        if ((pc->cache[j-1].expiration_ticks<now) && 
!strcmp(pc->cache[j-1].luser,luser)) {
+        if (*princ!=NULL) {
+            if 
(krb5_principal_compare(context,*princ,pc->cache[j-1].princ)) break;
+        } else {
+            krb5_copy_principal(context,pc->cache[j-1].princ,princ);
+            break;
+        }
+        }
+    }
+    }
+    HEIMDAL_MUTEX_unlock(&context->luser_principal_cache_mutex);
+    if (i==KRB5_LUSER_PRINCIPAL_CACHE_FANOUT) ret=ENOENT;
+    //{ char *ppn; if (ret) ppn = strdup("?"); else 
krb5_unparse_name(context,*princ,&ppn); printf("luser cache %s user %s 
<--> %s\n",ret?"miss":"hit",luser,ppn); free(ppn); }
+    return ret;
+}
+
+//if *luser!=NULL, return 0 only if the luser<->princ match is in the 
cache, else ENOENT.
+//if *luser==NULL, if princ is in the cache, set luser to the first hit 
and return 0, else ENOENT
+krb5_error_code  _krb5_luser_principal_cache_query_princ(krb5_context 
context,krb5_const_principal princ,char **luser) {
+    int ret,i,j; unsigned int h;
+    struct _krb5_luser_principal_cache *pc;
+    struct _krb5_luser_principal_cache_entry *pce;
+    clock_t now;
+    pc = context->luser_principal_cache;
+    if ((pc==NULL) || (princ==NULL) || (context==NULL) || 
(luser==NULL)) return ENOENT;
+    ret = 0;
+    h = princ_hash(princ);
+    now = clock();
+    HEIMDAL_MUTEX_lock(&context->luser_principal_cache_mutex);
+    
for(i=0;i<KRB5_LUSER_PRINCIPAL_CACHE_FANOUT;i++,h=(h+1)%KRB5_LUSER_PRINCIPAL_CACHE_HASHSIZE){
+    j=pc->principal_hash_table[h];
+    if (j!=0) {
+        if ((pc->cache[j-1].expiration_ticks<now) && 
krb5_principal_compare(context,princ,pc->cache[j-1].princ)) {
+
+        if (*luser!=NULL) {
+            if (strcmp(pc->cache[j-1].luser,*luser)) break;
+        } else {
+            *luser = strdup(pc->cache[j-1].luser);
+            break;
+        }
+        }
+    }
+    }
+    HEIMDAL_MUTEX_unlock(&context->luser_principal_cache_mutex);
+    if (i==KRB5_LUSER_PRINCIPAL_CACHE_FANOUT) ret=ENOENT;
+    //{ char *ppn; krb5_unparse_name(context,princ,&ppn); printf("princ 
cache %s user %s <--> %s\n",ret?"miss":"hit",ret? "?":*luser,ppn); 
free(ppn); }
+    return ret;
+}


diff -u /usr/src/kerberos5/lib/libgssapi_krb5/gss_krb5.c new/gss_krb5.c
--- /usr/src/kerberos5/lib/libgssapi_krb5/gss_krb5.c    2009-08-03 
03:13:06.000000000 -0500
+++ new/gss_krb5.c    2011-10-24 17:47:21.407712267 -0500
@@ -33,6 +33,7 @@

  #include <krb5.h>
  #include <roken.h>
+#include <der.h>

  OM_uint32
  gss_krb5_copy_ccache(OM_uint32 *minor_status,
diff -u /usr/src/kerberos5/lib/libgssapi_krb5/pname_to_uid.c 
new/pname_to_uid.c
--- /usr/src/kerberos5/lib/libgssapi_krb5/pname_to_uid.c    2009-08-03 
03:13:06.000000000 -0500
+++ new/pname_to_uid.c    2011-10-24 17:47:35.422326641 -0500
@@ -37,7 +37,7 @@
      krb5_context context;
      krb5_const_principal name = (krb5_const_principal) pname;
      krb5_error_code kret;
-    char lname[MAXLOGNAME + 1], buf[128];
+    char lname[MAXLOGNAME + 1], buf[1024];
      struct passwd pwd, *pw;

      GSSAPI_KRB5_INIT (&context);
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.