Use PAM uid, POSIX ACL's

"Lanclos, Jason" <[email protected]>
Newsgroups gmane.comp.telephony.fax.hylafax.devel
Message-ID <C873311FC373AD40BCF51C51E3DAD4800131E2A8BF@Autolycus.ldaf.state.la.us>
I have HylaFAX 6.0.3 installed and running on CentOS 5.3 with Samba/winbind setup to utilized user accounts in Active Directory.
Winbind generates the users UID, and does group membership lookups.  

When enabling Job Protection in HylaFAX, and setting the group id on the files to the User's UID, winbind would start to fail its lookups.
I presume this is because accounts provided by winbind do not have GID's that match the UID.
In order to get around this, I enabled POSIX ACLs on the file system, and setup the following permissions:

# file: var/spool/hylafax/recvq
# owner: uucp
# group: uucp
user::rwx
group::rwx
mask::rwx
other::r-x
default:user::rwx
default:user:uucp:rwx
default:group::rwx
default:mask::rwx
default:other::---

Example of file in revcq
# file: fax000060498.tif
# owner: jlanclos
# group: uucp
user::rw-
user:uucp:rwx
group::rw-
mask::rwx
other::---

HylaFAX runs as the uucp user, so it still has access to all the files, while allowing the User to be the file owner.
The included patch enables HylaFAX to work correctly with this setup, and allows permissions to be granted on a group level for the faxes.
It also gets the UID from PAM if it is not found in the hosts.hfaxd file.  It does require hfaxd to be linked with libacl.
hylafax.patch (application/octet-stream, 12.5 KB)
diff -ru --new-file hylafax-6.0.3/hfaxd/FileCache.c++ hylafax-6.0.3-ldaf/hfaxd/FileCache.c++
--- hylafax-6.0.3/hfaxd/FileCache.c++	1999-06-13 02:41:12.000000000 -0500
+++ hylafax-6.0.3-ldaf/hfaxd/FileCache.c++	2009-10-23 13:38:11.000000000 -0500
@@ -30,6 +30,7 @@
 FileCache* FileCache::cache[4096];	// cache of stat results
 #define	CACHESIZE	(sizeof (cache) / sizeof (cache[0]))
 
+aclcache_t FileCache::aclcache;
 					// statistics
 u_int FileCache::lookups = 0;		// total # lookups
 u_int FileCache::hits = 0;		// # lookups that hit in the cache
@@ -97,7 +98,7 @@
 	probes++;
 	if (fi->name == pathname) {
 	    fi->serial = master++;
-	    sb = fi->sb;
+	    sb = fi->sb;		
 	    hits++;
 	    return (true);
 	}
@@ -120,6 +121,7 @@
 	fi->name = pathname;
 	fi->serial = master++;
 	fi->sb = sb;
+	update_acl(pathname,sb);
     }
     return (true);
 }
@@ -141,6 +143,7 @@
 	if (fi->name == pathname) {
 	    hits++;
 	    fi->sb.st_mode = (fi->sb.st_mode&~0777) | (mode&0777);
+	    update_acl(pathname,fi->sb);
 	    break;
 	}
 	h = (u_int)(h*h) % CACHESIZE;
@@ -180,6 +183,7 @@
 		hits++;
 		fi->sb.st_uid = uid;
 		fi->sb.st_gid = gid;
+		update_acl(pathname,fi->sb);
 		break;
 	    }
 	    h = (u_int)(h*h) % CACHESIZE;
@@ -207,6 +211,7 @@
 		hits++;
 		fi->serial = master++;
 		fi->sb = sb;
+		update_acl(pathname,sb);	
 		return (true);
 	    } else {
 		flushed++;
@@ -234,6 +239,7 @@
 	fi->name = pathname;
 	fi->serial = master++;
 	fi->sb = sb;
+	update_acl(pathname,sb);	
     }
     return (true);
 }
@@ -247,6 +253,7 @@
     while (fi && --maxprobes) {
 	if (fi->name == pathname) {
 	    flushed++;
+	    flush_acl(fi->sb.st_ino);
 	    cache[h] = NULL;
 	    delete fi;
 	    break;
@@ -255,3 +262,67 @@
 	fi = cache[h];
     }
 }
+
+
+
+
+acl_t
+FileCache::lookup_acl(const ino_t inode) 
+{
+
+  acl_t acl = aclcache[inode];
+  if (!acl) 
+   return (NULL);
+ 
+  return acl;
+}
+
+
+acl_t
+FileCache::lookup_acl(const char* pathname, struct stat& sb)
+{
+   int r;
+   acl_t acl = aclcache[sb.st_ino];
+   if (!acl) {
+	 acl = acl_get_file(pathname,ACL_TYPE_ACCESS);
+	 aclcache[sb.st_ino]=acl;
+  }
+  return acl;
+}
+
+bool
+FileCache::update_acl(const char* pathname, struct stat& sb)
+{
+
+   int r;
+  acl_t oldacl = aclcache[sb.st_ino];
+  acl_free(oldacl);
+//   if (!acl) {
+	 acl_t acl = acl_get_file(pathname,ACL_TYPE_ACCESS);
+	if (acl) {
+	 aclcache[sb.st_ino]=acl;
+	return true;
+	}
+
+  //}
+  return false;
+}
+void
+FileCache::flush_acl(const ino_t inode)
+{
+  acl_t acl=lookup_acl(inode);
+  acl_free(acl);
+  aclcache.erase(inode);
+
+}
+void
+FileCache::reset_acl(void) {
+
+  
+  aclcache_t::iterator it;
+
+   for (it=aclcache.begin() ; it != aclcache.end(); it++) 
+	flush_acl((*it).first);
+   
+}
+
diff -ru --new-file hylafax-6.0.3/hfaxd/FileCache.h hylafax-6.0.3-ldaf/hfaxd/FileCache.h
--- hylafax-6.0.3/hfaxd/FileCache.h	1999-06-13 02:41:12.000000000 -0500
+++ hylafax-6.0.3-ldaf/hfaxd/FileCache.h	2009-10-23 13:18:44.000000000 -0500
@@ -28,15 +28,22 @@
 
 #include "Str.h"
 #include <sys/stat.h>
-
+#include <stdlib.h>
+#include <iostream>
+#include <sys/acl.h>
+#include <acl/libacl.h>
+#include <map>
 /*
  * Cache to reduce the number of stat system calls.
  */
+
+typedef std::map<ino_t,acl_t> aclcache_t;
+
 struct FileCache {
     fxStr	name;
     struct stat sb;
     u_int	serial;
-
+    static aclcache_t aclcache;
     static u_int master;		// master serial # generator
     static FileCache* cache[4096];
 					// statistics
@@ -60,5 +67,14 @@
     static bool chmod(const char* pathname, mode_t mode);
     static bool chown(const char* pathname, uid_t uid, gid_t gid);
     static void reset(void);
+    static acl_t lookup_acl(const ino_t inode);
+    static acl_t lookup_acl(const char* pathname, struct stat& sb);
+    static bool update_acl(const char* pathname, struct stat& sb);
+    static void flush_acl(const ino_t inode);
+    static void reset_acl(void);	
 };
+
+
+
+
 #endif /* _FileCache_ */
diff -ru --new-file hylafax-6.0.3/hfaxd/FileSystemACL.c++ hylafax-6.0.3-ldaf/hfaxd/FileSystemACL.c++
--- hylafax-6.0.3/hfaxd/FileSystemACL.c++	1969-12-31 18:00:00.000000000 -0600
+++ hylafax-6.0.3-ldaf/hfaxd/FileSystemACL.c++	2009-10-23 13:42:17.000000000 -0500
@@ -0,0 +1,80 @@
+#include "HylaFAXServer.h"
+#include "Sys.h"
+
+#include <sys/acl.h>
+#include <acl/libacl.h>
+#include <pwd.h>
+#include <grp.h>
+
+bool
+HylaFAXServer::checkFileRights_acl(int op,const struct stat& sb) 
+{
+  if (!usefsacls) 
+     return false;
+
+  bool grantaccess,checkthisacl,mask,founduseracl = false;
+  bool maskok = true;
+  acl_entry_t ent;
+  acl_t acl;
+  int r;
+  const char* username=userName(uid); 
+  acl=FileCache::lookup_acl(sb.st_ino);
+
+  if (acl != NULL)
+	r = acl_get_entry(acl, ACL_FIRST_ENTRY,&ent);
+  if (r != 1)
+	return false;
+  while ( r > 0 && maskok) {
+	const id_t *id_p;
+	uid_t id;
+	checkthisacl = mask = false;
+	acl_tag_t e_type;
+	acl_get_tag_type(ent,&e_type);
+	id_p = (const id_t*) acl_get_qualifier(ent);
+	if (id_p) {
+   	  id = *id_p;
+	switch (e_type) {
+		case ACL_USER:
+			if (id == uid) 
+			   checkthisacl=founduseracl=true;
+			break;
+		case ACL_GROUP:
+			if (id_p && !founduseracl) {
+			  struct group *grp;
+			  grp = getgrgid(id);
+			  for ( ; NULL != *(grp->gr_mem); (grp->gr_mem)++ ) {
+				if ( !strcmp(*(grp->gr_mem),username) ) 
+				   checkthisacl=true;
+			  }
+			}
+			break;
+		case ACL_MASK:
+			mask=checkthisacl=true;
+			break;
+	}
+	if (checkthisacl) {
+	   acl_permset_t permset;
+	   acl_get_permset(ent,&permset);
+	   if (acl_get_perm(permset,op) && !mask) 
+	   	grantaccess=true;
+	   if (mask) 
+	      if (!acl_get_perm(permset,op)) 
+		grantaccess=maskok=false;
+	}
+	}
+	acl_free((void*)id_p);
+	acl_free(&ent);
+	r = acl_get_entry(acl,ACL_NEXT_ENTRY,&ent);
+  }
+  return grantaccess;
+}	
+
+
+
+
+
+
+
+
+
+
diff -ru --new-file hylafax-6.0.3/hfaxd/FileSystemACL.h hylafax-6.0.3-ldaf/hfaxd/FileSystemACL.h
--- hylafax-6.0.3/hfaxd/FileSystemACL.h	1969-12-31 18:00:00.000000000 -0600
+++ hylafax-6.0.3-ldaf/hfaxd/FileSystemACL.h	2009-10-22 10:45:10.000000000 -0500
@@ -0,0 +1,12 @@
+#include <map>
+
+extern "C" {
+#include <sys/acl.h>
+#include <acl/libacl.h>
+#include <pwd.h>
+#include <grp.h>
+}
+
+
+
+
diff -ru --new-file hylafax-6.0.3/hfaxd/FileSystem.c++ hylafax-6.0.3-ldaf/hfaxd/FileSystem.c++
--- hylafax-6.0.3/hfaxd/FileSystem.c++	2009-07-03 14:36:47.000000000 -0500
+++ hylafax-6.0.3-ldaf/hfaxd/FileSystem.c++	2009-10-23 13:41:48.000000000 -0500
@@ -88,10 +88,19 @@
 	    id = (u_int) atoi(user);
 	else if (!userID(user, id))
 	    return;
-	if (!FileCache::chown(pathname, sb.st_uid, (gid_t) id))
+	if (usefsacls) {  
+	   if (!FileCache::chown(pathname, sb.st_uid, (gid_t) faxuid))
 	    perror_reply(550, pathname, errno);
-	else
+	  else
+	    ack(250, cmdToken(T_CHOWN));
+	} else {
+	   if (!FileCache::chown(pathname, sb.st_uid, (gid_t) id))
+	    perror_reply(550, pathname, errno);
+	  else
 	    ack(250, cmdToken(T_CHOWN));
+	
+	}
+	
     }
 }
 
@@ -255,10 +264,18 @@
     if (sb.st_mode & op)		// public access
 	return (true);
 
-    if ((sb.st_gid==uid) && ((sb.st_mode>>3)&op))	// owner access
-	return (true);
-    return false;
+    if (usefsacls) {
+	if ((sb.st_uid==uid) && ((sb.st_mode>>3)&op))	// owner access
+	   return (true);
+      } else {
+	  if ((sb.st_gid==uid) && ((sb.st_mode>>3)&op))	// owner access
+   	   return (true);
+       }
+   
+    if(checkFileRights_acl(op,sb)) 
+	 return (true);
 
+    return false;
 }
 
 /*
@@ -285,7 +302,11 @@
 	 * successful file creation--for use below.
 	 */
 	sb.st_mode = S_IFREG|S_IRGRP|S_IWGRP;
-	sb.st_gid = (gid_t) uid;
+	if (usefsacls) 
+	  sb.st_uid = (gid_t) uid;
+	 else 
+	  sb.st_gid = (gid_t) uid;
+	
 	sb.st_ino = 0;					// NB: to be created
     }
     /*
@@ -331,10 +352,11 @@
 		perror_reply(550, path, EPERM);		// cannot stor in dir.
 		return (NULL);
 	    }
-	    if (!checkFileRights(op, sb)) {
-		perror_reply(550, path, EPERM);
-		return (NULL);
-	    }
+               if(!checkFileRights(op,sb)) 
+		 if (!checkFileRights_acl(op,sb)) {
+		     perror_reply(550, path, EPERM);
+		     return (NULL);
+	        } 
 	}
     } else {						// privileged client
 	if (op != X_OK && S_ISDIR(sb.st_mode)) {	// cannot r/w directory
@@ -394,7 +416,8 @@
         if (!FileCache::lookup(file, sb)) {
             fatal("setFileOwner called for non-existent file (check)");
         }
-        if (sb.st_gid != (gid_t) uid) {
+        
+	if (sb.st_uid != (gid_t) uid) {
             state |= S_SETGID;			// not set, must force it
         } else {
             state &= ~S_SETGID;			// set by OS, no work to do
@@ -410,9 +433,15 @@
         if (!FileCache::lookup(file, sb)) {
             fatal("setFileOwner called for non-existent file (set)");
         }
-        if (!FileCache::chown(file, sb.st_uid, (gid_t) uid)) {
+	if (usefsacls) {
+           if (!FileCache::chown(file, uid, (gid_t) faxuid)) {
             logError("%s: chown: %s", file, strerror(errno));
-        }
+           } 
+	} else { 
+	  if (!FileCache::chown(file, sb.st_uid, (gid_t) uid)) {
+            logError("%s: chown: %s", file, strerror(errno));
+           }
+	 }
     }
 }
 
@@ -654,7 +683,10 @@
 		buf.fput(fspec, (u_int) sb.st_size);		// XXX
 		break;
 	    case 'u':
-		buf.fput(fspec, (u_int) sb.st_uid);
+		if (usefsacls)
+		  buf.fput(fspec, (u_int) sb.st_uid);
+		else
+		  buf.fput(fspec, (u_int) sb.st_gid);
 		break;
 	    }
 	} else
diff -ru --new-file hylafax-6.0.3/hfaxd/HylaFAXServer.c++ hylafax-6.0.3-ldaf/hfaxd/HylaFAXServer.c++
--- hylafax-6.0.3/hfaxd/HylaFAXServer.c++	2009-05-15 12:35:39.000000000 -0500
+++ hylafax-6.0.3-ldaf/hfaxd/HylaFAXServer.c++	2009-10-23 13:26:36.000000000 -0500
@@ -653,6 +655,8 @@
 { "allowsorting",	&HylaFAXServer::allowSorting,		true },
 { "publicjobq",		&HylaFAXServer::publicJobQ,		false },
 { "publicrecvq",	&HylaFAXServer::publicRecvQ,		false },
+{ "usefsacls",         &HylaFAXServer::usefsacls,              false },
+{ "usepamuid",         &HylaFAXServer::usePAMuid,              false },
 };
 
 void
diff -ru --new-file hylafax-6.0.3/hfaxd/HylaFAXServer.h hylafax-6.0.3-ldaf/hfaxd/HylaFAXServer.h
--- hylafax-6.0.3/hfaxd/HylaFAXServer.h	2009-05-15 12:35:39.000000000 -0500
+++ hylafax-6.0.3-ldaf/hfaxd/HylaFAXServer.h	2009-10-23 13:59:05.000000000 -0500
@@ -236,6 +236,8 @@
     bool	publicJobQ;		// Public/protection on recvq?
     bool	publicRecvQ;		// Public/protection on recvq?
     bool	allowSorting;		// Allow client to make us sort
+    bool	usePAMuid;		// Use PAM uid
+    bool	usefsacls;		// Use Extended ACLs
     /*
      * User authentication and login-related state.
      */
@@ -603,6 +603,9 @@
     void getServerStatus(const char* fileName, fxStr& status);
     void Mprintf(FILE*, const char*, const ModemConfig&);
     void Mprintf(fxStackBuffer&, const char*, const ModemConfig&);
+
+    bool checkFileRights_acl(int op,const struct stat& sb); 
+
 public:
     HylaFAXServer();
     virtual ~HylaFAXServer();
diff -ru --new-file hylafax-6.0.3/hfaxd/RecvQueue.c++ hylafax-6.0.3-ldaf/hfaxd/RecvQueue.c++
--- hylafax-6.0.3/hfaxd/RecvQueue.c++	2009-05-15 12:35:39.000000000 -0500
+++ hylafax-6.0.3-ldaf/hfaxd/RecvQueue.c++	2009-10-23 12:55:16.000000000 -0500
@@ -464,7 +469,10 @@
 		buf.fput(fspec, (u_int) sb.st_size);		// XXX
 		break;
 	    case 'o':
-		buf.fput(fspec, userName((u_int) sb.st_gid));
+		if (usefsacls)
+		  buf.fput(fspec, userName((u_int) sb.st_uid));
+		else 
+		  buf.fput(fspec, userName((u_int) sb.st_gid));
 		break;
 	    case 'p':
 		buf.fput(fspec, ri.npages);
diff -ru --new-file hylafax-6.0.3/hfaxd/User.c++ hylafax-6.0.3-ldaf/hfaxd/User.c++
--- hylafax-6.0.3/hfaxd/User.c++	2009-05-15 12:35:39.000000000 -0500
+++ hylafax-6.0.3-ldaf/hfaxd/User.c++	2009-10-14 17:08:30.000000000 -0500
@@ -62,9 +62,15 @@
 	logError("Unable to open the user access file %s: %s",
 	    (const char*) userAccessFile, strerror(errno));
 
-    if (! check)
+    if (! check) { 
 	check = checkuserPAM(name);
 
+       if (usePAMuid) {
+	struct passwd *pwd = NULL;
+	  pwd = getpwnam(name);
+	  uid = pwd->pw_uid;
+     }
+   }
     /*
      * This causes the user to be prompted for a password
      * and then denied access.  We do this to guard against
@@ -265,6 +271,14 @@
     if (idcache == NULL)			// load cache from file
 	fillIDCache();
     const fxStr* hit = idcache->find(id);	// check cache
+    if (!hit && usePAMuid) {
+        struct passwd *pwd = NULL;
+        pwd = getpwuid(id);
+	if (pwd) {
+           (*idcache)[id]= fxStr(pwd->pw_name);
+	   hit = idcache->find(id);
+	}					// new entry
+    }	
     if (!hit) {					// create entry w/ numeric value
 	(*idcache)[id] = fxStr((int) id, "%u");
 	hit = idcache->find(id);		// new entry
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.