--- globus_gss_assist-4.0/gridmap.c	1 Nov 2007 04:04:46 -0000	1.19.4.3
+++ globus_gss_assist-4.0/gridmap.c	30 Jan 2008 19:34:57 -0000
@@ -107,6 +107,393 @@
 
 #endif
 
+
+
+/******************************************************************************
+                       Start of gridmapdir functions
+
+ These all use the environment variable GRIDMAPDIR
+  (a) if not set, then the gridmapdir functions are not used
+  (b) the value it is set to defines the gridmap directory
+      (eg export GRIDMAPDIR=/etc/grid-security/gridmapdir)
+
+******************************************************************************/
+
+#include <utime.h>
+#include <errno.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <pwd.h>
+#include <sys/types.h>
+
+/******************************************************************************
+Function:   gridmapdir_otherlink
+Description:
+        find another link in GRIDMAPDIR to the same inode as firstlink
+        and change the modification time of firstlink to now (so that we
+        always know when this pair was last used)
+        
+Parameters:
+        firstlink, the filename of the link we already know
+
+Returns:
+        a pointer to the other link's filename (without path) or NULL if none
+        found (this is malloc'd and will need freeing)
+
+******************************************************************************/
+static char 
+*gridmapdir_otherlink(char *   firstlink)
+{
+     int            ret;
+     char           *firstlinkpath, *otherlinkdup, *otherlinkpath,
+                    *gridmapdir;
+     struct dirent  *gridmapdirentry;
+     DIR            *gridmapdirstream;
+     struct stat    statbuf;
+     ino_t          firstinode;
+
+     gridmapdir = getenv("GRIDMAPDIR");
+     if (gridmapdir == NULL) return NULL;
+     
+     firstlinkpath = malloc(strlen(gridmapdir) + 2 + strlen(firstlink));
+     sprintf(firstlinkpath, "%s/%s", gridmapdir, firstlink);     
+     ret = stat(firstlinkpath, &statbuf);
+     free(firstlinkpath);   
+     if (ret != 0) return NULL;
+     if (statbuf.st_nlink != 2) return NULL;
+     
+     firstinode = statbuf.st_ino; /* save for comparisons */
+          
+     gridmapdirstream = opendir(gridmapdir);
+
+     if (gridmapdirstream != NULL)
+     {
+         while ((gridmapdirentry = readdir(gridmapdirstream)) != NULL)
+         {       
+                 if (strcmp(gridmapdirentry->d_name, firstlink) == 0) continue;
+           
+                 otherlinkpath = malloc(strlen(gridmapdir) + 2 + 
+                                        strlen(gridmapdirentry->d_name));
+                 sprintf(otherlinkpath, "%s/%s", gridmapdir, 
+                                            gridmapdirentry->d_name);
+
+                 ret = stat(otherlinkpath, &statbuf);     
+                 if ((ret == 0) && (statbuf.st_ino == firstinode))
+                 {
+                      utime(otherlinkpath, (struct utimbuf *) NULL);
+                      free(otherlinkpath);
+                      otherlinkdup = strdup(gridmapdirentry->d_name);
+                      closedir(gridmapdirstream);     
+                      return otherlinkdup;
+                 }
+                 else free(otherlinkpath);
+         }
+         
+         closedir(gridmapdirstream);     
+     }
+
+     return NULL;
+}
+
+/******************************************************************************
+Function:   gridmapdir_urlencode
+Description:
+        Convert string to URL encoded and return pointer to the encoded
+        version, obtained through malloc. Calling routine must free
+        this. Here "URL encoded" means anything other than an isalnum()
+        goes to %HH where HH is its ascii value in hex; also A-Z => a-z 
+        This name is suitable for filenames since no / or spaces.
+
+Parameters:
+        rawstring, the string to be converted
+
+Returns:
+        a pointer to the encoded string or NULL if the malloc failed
+
+******************************************************************************/
+static char 
+*gridmapdir_urlencode(char * rawstring)
+{
+     int          encodedchar = 0, rawchar = 0;
+     char *       encodedstring;
+     
+     encodedstring = (char *) malloc(3 * strlen(rawstring) + 1);
+     
+     if (encodedstring == NULL) return (char *) NULL;
+
+     while (rawstring[rawchar] != '\0')
+     {
+           if (isalnum(rawstring[rawchar]))
+           {
+               encodedstring[encodedchar] = tolower(rawstring[rawchar]);
+               ++rawchar;
+               ++encodedchar;
+           }
+           else
+           {
+               sprintf(&encodedstring[encodedchar], "%%%02x", 
+                                               rawstring[rawchar]);
+               ++rawchar;
+               encodedchar = encodedchar + 3;
+           }        
+     }
+
+     encodedstring[encodedchar] = '\0';
+     
+     return encodedstring;
+}
+
+/******************************************************************************
+Function:   gridmapdir_newlease
+Description:
+        Search for an unleased local username to give to the globus user
+        corresponding to encodedfilename, and then lease it.
+
+Parameters: 
+        encodedfilename, URL-encoded globus client name and pathname of 
+           the globus user who requested authentication 
+        usernameprefix, the prefix of acceptable usernames (or "\0")
+
+Returns:
+        no return value
+******************************************************************************/
+
+void
+gridmapdir_newlease(char *     encodedglobusidp,
+                    char *     usernameprefix)
+{
+     int            ret;
+     char           *userfilename, *encodedfilename, *gridmapdir;
+     struct dirent  *gridmapdirentry;
+     DIR            *gridmapdirstream;
+     struct stat    statbuf;
+     
+     gridmapdir = getenv("GRIDMAPDIR");
+     if (gridmapdir == NULL) return;
+
+     encodedfilename = malloc(strlen(gridmapdir) + (size_t) 2 + 
+                              strlen(encodedglobusidp));
+     sprintf(encodedfilename, "%s/%s", gridmapdir, encodedglobusidp);
+
+     gridmapdirstream = opendir(gridmapdir);
+
+     while ((gridmapdirentry = readdir(gridmapdirstream)) != NULL)
+     {
+       /* we dont want any files that dont look like acceptable usernames */
+       if ((*(gridmapdirentry->d_name) == '%') || 
+           (strcmp(gridmapdirentry->d_name, "root") == 0))   continue;
+       else if (*(gridmapdirentry->d_name) == '.')           continue;
+       else if (index(gridmapdirentry->d_name, '~') != NULL) continue;
+       else if (strncmp(gridmapdirentry->d_name, usernameprefix,
+                        strlen(usernameprefix)) != 0)        continue;
+
+       userfilename = malloc(strlen(gridmapdir) + (size_t) 2 + 
+                             strlen(gridmapdirentry->d_name));
+       sprintf(userfilename, "%s/%s", gridmapdir, gridmapdirentry->d_name);
+       stat(userfilename, &statbuf);
+       
+       if (statbuf.st_nlink == 1) /* this one isnt leased yet */
+       {   
+           ret = link(userfilename, encodedfilename);
+           free(userfilename);
+           if (ret != 0) 
+           {
+               /* link failed: this is probably because a VERY lucky
+                  other process has obtained a lease for encodedfilename 
+                  while we were faffing around */
+               closedir(gridmapdirstream);
+               free(encodedfilename);
+               return;
+           }
+     
+           stat(encodedfilename, &statbuf);
+           if (statbuf.st_nlink > 2) 
+           {
+              /* two globusIDs have grabbed the same username: back off */
+              unlink(encodedfilename);
+              continue;
+           }
+
+           closedir(gridmapdirstream);
+           free(encodedfilename);
+           return; /* link worked ok, so return */
+       }
+       else free(userfilename); /* already in use, try next one */
+     }
+     
+     closedir(gridmapdirstream);
+     free(encodedfilename);
+     return; /* no unleased names left: give up */     
+}
+     
+/******************************************************************************
+Function:   gridmapdir_userid
+Description:
+        This is equivalent to globus_gss_assist_gridmap but for the dynamic
+        user ids in the gridmapdir: maps a globusID to a local unix user id,
+        either one already leased, or calls gridmapdir_newlease() to obtain 
+        a new lease. This is called by globus_gss_assist_gridmap if the 
+        local user id in the static gridmap file begins . (for a dynamic id)
+
+Parameters: 
+        globusidp, globus client name who requested authentication 
+        usernameprefix, prefix of the local usernames which would 
+               be acceptable (or "\0" )
+        *userid returned userid name for local system. 
+
+Returns:
+       
+        0 on success
+        !=0 on failure
+
+******************************************************************************/
+
+static int
+gridmapdir_userid(char *     globusidp,
+                  char *     usernameprefix,
+                  char **    useridp)
+{
+     char             *encodedglobusidp;
+     
+     if (getenv("GRIDMAPDIR") == NULL) return 1; /* GRIDMAPDIR defined? */
+
+     if (globusidp[0] != '/') return 1; /* must be a proper subject DN */
+     
+     encodedglobusidp = gridmapdir_urlencode(globusidp);
+
+     *useridp = gridmapdir_otherlink(encodedglobusidp);
+
+     if (*useridp == NULL) /* maybe no lease yet */
+     {
+         gridmapdir_newlease(encodedglobusidp, usernameprefix); 
+         /* try making a lease */
+         
+         *useridp = gridmapdir_otherlink(encodedglobusidp); 
+         /* check if there is a now a lease - possibly made by someone else */
+
+         if (*useridp == NULL) 
+         {
+             free(encodedglobusidp);
+             return 1; /* still no good */
+         }
+     }
+
+     free(encodedglobusidp);
+     return 0;
+}
+
+/******************************************************************************
+Function:   gridmapdir_globusid
+Description:
+        This is equivalent to globus_gss_assist_map_local_user but for the 
+        dynamic user ids in the gridmapdir: search through leases to find
+        which globusID corresponds to a local unix user id.
+        This is called by globus_gss_assist_map_local_user 
+
+Parameters: 
+        globus client name who requested authentication 
+        *userid returned userid name for local system. 
+
+Returns:
+       
+        0 on success
+        !=0 on failure
+
+******************************************************************************/
+
+static int
+gridmapdir_globusid(char *     useridp,
+                    char **    globusidp)
+{
+     int              encodedptr = 0, decodedptr = 0;
+     char             *encodedglobusidp;
+     
+     if (useridp[0] == '/') return 1; /* must not be a subject DN */
+     
+     encodedglobusidp = gridmapdir_otherlink(useridp);
+
+     if (encodedglobusidp == NULL) return 1; /* not leased */
+     
+     *globusidp = malloc(strlen(encodedglobusidp));
+     
+     while (encodedglobusidp[encodedptr] != '\0')
+     {
+            if (encodedglobusidp[encodedptr] != '%')
+            {
+                (*globusidp)[decodedptr] = encodedglobusidp[encodedptr];
+                ++encodedptr;
+                ++decodedptr;
+            }
+            else /* must be a %HH encoded character */
+            {
+                /* even paranoids have enemies ... */
+                if (encodedglobusidp[encodedptr+1] == '\0') break;
+                if (encodedglobusidp[encodedptr+2] == '\0') break;
+
+                (*globusidp)[decodedptr] = 
+                   globus_i_gss_assist_xdigit_to_value(encodedglobusidp[encodedptr+1]) * 16 +
+                   globus_i_gss_assist_xdigit_to_value(encodedglobusidp[encodedptr+2]);
+
+                encodedptr = encodedptr + 3;
+                ++decodedptr;
+            }
+     }
+              
+     free(encodedglobusidp);
+     (*globusidp)[decodedptr] = '\0';
+     return 0;
+}
+
+/******************************************************************************
+Function:   gridmapdir_userok
+Description:
+        This is equivalent to globus_gss_assist_userok but for the dynamic
+        user ids in the gridmapdir: finds the local unix username leased to 
+        a globusID and compare with the username being checked.
+        This is called by globus_gss_assist_userok if the local user id in
+        the static gridmap file is -  (for a dynamic id)
+
+Parameters: 
+        globus client name who requested authentication 
+        userid to be checked
+
+Returns:
+        0 on success (authorization allowed)
+        !=0 on failure or authorization denied
+                
+******************************************************************************/
+
+static int
+gridmapdir_userok(char *     globusidp,
+                  char *     userid)
+{
+     char                    *encodedglobusidp, *leasedname;
+     
+     if (globusidp[0] != '/') return 1; /* must be a proper subject DN */
+     
+     encodedglobusidp = gridmapdir_urlencode(globusidp);
+     leasedname       = gridmapdir_otherlink(encodedglobusidp);
+     free(encodedglobusidp);
+
+     if (leasedname == NULL) return 1;
+
+     if (strcmp(userid, leasedname) == 0)
+     {
+         free(leasedname);
+         return 0;
+     }
+     else
+     {
+         free(leasedname);
+         return 1;
+     }
+}
+
+/******************************************************************************
+                     End of gridmapdir functions
+******************************************************************************/
+
 /**
  * @name Gridmap
  */
@@ -147,6 +534,8 @@
     globus_result_t                     result = GLOBUS_SUCCESS;
     globus_i_gss_assist_gridmap_line_t *
                                         gline = NULL;
+    char                               *usernameprefix;
+    int                                 ret;
 
     static char *                       _function_name_ =
     "globus_gss_assist_gridmap";
@@ -203,6 +592,16 @@
                 (_GASL("Duplicate string operation failed")));
 	    goto exit;
 	}
+
+	if ((*useridp)[0] == '.') /* need to use gridmapdir */
+	{             
+	    usernameprefix = strdup(&((*useridp)[1]));
+	    free(*useridp); *useridp = NULL;
+	    ret = gridmapdir_userid(globusidp, usernameprefix, useridp);
+	    free(usernameprefix);
+	    return ret;
+        }
+
     }
     else
     {
@@ -314,6 +713,12 @@
         goto exit;
     }
 
+    if (*((gline->user_ids)[0]) == '.') /* try using gridmapdir */ 
+    {
+        globus_i_gss_assist_gridmap_line_free(gline);
+        return gridmapdir_userok(globusid, userid);
+    }
+    else
     for (useridp = gline->user_ids; *useridp != NULL; useridp++)
     {
 	if (strcmp(*useridp, userid) == 0)
@@ -480,7 +885,8 @@
         error_obj = globus_error_get(result);
         globus_object_free(error_obj);
 
-        return 1;
+        /* try with gridmapdir before giving up completely */
+        return gridmapdir_globusid(local_user, globusidp);
     }
 } 
 /* globus_gss_assist_map_local_user() */
