Add the ability to control locks, from Jens Sorensen.
diff -uNr Linux-PAM-0.75/modules/pam_limits/limits.skel Linux-PAM-0.75/modules/pam_limits/limits.skel
--- Linux-PAM-0.75/modules/pam_limits/limits.skel	Tue Jun 20 18:11:41 2000
+++ Linux-PAM-0.75/modules/pam_limits/limits.skel	Tue Jul 10 00:14:23 2001
@@ -27,6 +27,7 @@
 #        - as - address space limit
 #        - maxlogins - max number of logins for this user
 #        - priority - the priority to run user process with
+#        - locks - max number of file locks the user can hold
 #
 #<domain>      <type>  <item>         <value>
 #
diff -uNr Linux-PAM-0.75/modules/pam_limits/pam_limits.c Linux-PAM-0.75/modules/pam_limits/pam_limits.c
--- Linux-PAM-0.75/modules/pam_limits/pam_limits.c	Wed Mar 28 23:21:16 2001
+++ Linux-PAM-0.75/modules/pam_limits/pam_limits.c	Tue Jul 10 00:14:23 2001
@@ -46,6 +46,14 @@
 #define LIMITS_DEF_DEFAULT  2 /* limit was set by an default entry */
 #define LIMITS_DEF_NONE     3 /* this limit was not set yet */
 
+const char *limits_def_names[] = {
+	"USER",
+	"GROUP",
+	"DEFAULT",
+	"NONE",
+	NULL,
+};
+
 struct user_limits_struct {
     int src_soft;
     int src_hard;
@@ -76,6 +84,13 @@
 #include <security/pam_modules.h>
 #include <security/_pam_macros.h>
 
+#ifdef HAVE_PWDBLIB
+#include <pwdb/pwdb_map.h>
+#else
+#include <pwd.h>
+#include <grp.h>
+#endif
+
 /* logging */
 static void _pam_log(int err, const char *format, ...)
 {
@@ -101,17 +116,17 @@
      for (ctrl=0; argc-- > 0; ++argv) {
 
           /* generic options */
-
           if (!strcmp(*argv,"debug"))
-               ctrl |= PAM_DEBUG_ARG;
+                ctrl |= PAM_DEBUG_ARG;
           else if (!strncmp(*argv,"conf=",5))
-                strcpy(pl->conf_file,*argv+5);
+                strncpy(pl->conf_file,*argv+5,sizeof(pl->conf_file) - 1);
 	  else if (!strncmp(*argv,"change_uid",10))
 		ctrl |= PAM_DO_SETREUID;
           else {
-               _pam_log(LOG_ERR,"pam_parse: unknown option; %s",*argv);
+                _pam_log(LOG_ERR,"pam_parse: unknown option; %s",*argv);
           }
      }
+     pl->conf_file[sizeof(pl->conf_file) - 1] = '\0';
 
      return ctrl;
 }
@@ -127,6 +142,8 @@
 #define LIMIT_ERR 1 /* error setting a limit */
 #define LOGIN_ERR 2 /* too many logins err */
 
+static int is_on_group(const char *user_name, const char *group_name);
+
 /* Counts the number of user logins and check against the limit*/
 static int check_logins(const char *name, int limit, int ctrl,
 			struct pam_limit_s *pl)
@@ -135,7 +152,8 @@
     unsigned int count;
 
     if (ctrl & PAM_DEBUG_ARG) {
-	 _pam_log(LOG_DEBUG, "checking logins for '%s' / %d\n", name,limit);
+	 _pam_log(LOG_DEBUG, "checking logins for '%s' (maximum of %d)\n",
+		  name, limit);
     }
 
     if (limit < 0)
@@ -154,17 +172,22 @@
 #endif
         if (ut->UT_USER[0] == '\0')
             continue;
-        if (!pl->flag_numsyslogins
-	    && strncmp(name, ut->UT_USER, sizeof(ut->UT_USER)) != 0)
-            continue;
-        if (++count >= limit)
+        if (!pl->flag_numsyslogins) {
+	    if ((pl->login_limit_def == LIMITS_DEF_USER)
+	        && strncmp(name, ut->UT_USER, sizeof(ut->UT_USER)) != 0)
+                continue;
+	    if ((pl->login_limit_def == LIMITS_DEF_GROUP)
+		&& !is_on_group(ut->UT_USER, name))
+                continue;
+	}
+        if (++count > limit)
             break;
     }
     endutent();
-    if (count >= limit) {
+    if (count > limit) {
 	if (name) {
-	    _pam_log(LOG_WARNING, "Too many logins (max %d) for %s",
-		     limit, name);
+	    _pam_log(LOG_WARNING, "Too many logins (max %d) for %s", limit,
+		     name);
 	} else {
 	    _pam_log(LOG_WARNING, "Too many system logins (max %d)", limit);
 	}
@@ -184,32 +207,33 @@
     return 0;
 }
 
-/* Checks if a user is a member of a group */
+/* Checks if a user is a member of a group, return non-zero if
+   the user is in the group. */
 static int is_on_group(const char *user_name, const char *group_name)
 {
     struct passwd *pwd;
     struct group *grp, *pgrp;
     char uname[LINE_LENGTH], gname[LINE_LENGTH];
     
+    if (!user_name)
+        return 0;
+    if (!group_name)
+        return 0;
     if (!strlen(user_name))
         return 0;
     if (!strlen(group_name))
         return 0;
     memset(uname, 0, sizeof(uname));
-    strncpy(uname, user_name, LINE_LENGTH);
+    strncpy(uname, user_name, sizeof(uname) - 1);
     memset(gname, 0, sizeof(gname));
-    strncpy(gname, group_name, LINE_LENGTH);
+    strncpy(gname, group_name, sizeof(gname) - 1);
         
-    setpwent();
     pwd = getpwnam(uname);
-    endpwent();
     if (!pwd)
         return 0;
 
     /* the info about this group */
-    setgrent();
     grp = getgrnam(gname);
-    endgrent();
     if (!grp)
         return 0;
     
@@ -218,9 +242,7 @@
         return 1;
 
     /* next check: user primary group is group_name ? */
-    setgrent();
     pgrp = getgrgid(pwd->pw_gid);
-    endgrent();
     if (!pgrp)
         return 0;
     if (!strcmp(pgrp->gr_name, gname))
@@ -236,8 +258,16 @@
 
     D(("called."));
 
-    for(i = 0; i < RLIM_NLIMITS; i++) 
-	retval |= getrlimit(i, &pl->limits[i].limit);
+    /* If we were built on 2.4 and are running on 2.2, some of these will
+     * give us EINVAL errors. */
+    for(i = 0; i < RLIM_NLIMITS; i++) {
+	int err = getrlimit(i, &pl->limits[i].limit);
+	if(err != 0) {
+            if(errno != EINVAL) {
+		retval |= -1;
+	    }
+	}
+    }
     
     pl->limits[RLIMIT_CPU].src_soft = LIMITS_DEF_NONE;
     pl->limits[RLIMIT_CPU].src_hard = LIMITS_DEF_NONE;
@@ -269,6 +299,11 @@
     pl->limits[RLIMIT_AS].src_soft = LIMITS_DEF_NONE;
     pl->limits[RLIMIT_AS].src_hard = LIMITS_DEF_NONE;
 
+#ifdef RLIMIT_LOCKS
+    pl->limits[RLIMIT_LOCKS].src_soft = LIMITS_DEF_NONE;
+    pl->limits[RLIMIT_LOCKS].src_hard = LIMITS_DEF_NONE;
+#endif
+
     pl->priority = 0;
     pl->login_limit = -2;
     pl->login_limit_def = LIMITS_DEF_NONE;
@@ -286,9 +321,13 @@
     const char **endptr = &lim_value;
     const char *value_orig = lim_value;
         
+    /* avoid stupid warning about const manipulation */
+    endptr = &lim_value;
+    
     if (ctrl & PAM_DEBUG_ARG)
-	 _pam_log(LOG_DEBUG, "%s: processing(%d) %s %s %s\n",
-		  __FUNCTION__,source,lim_type,lim_item,lim_value);
+	 _pam_log(LOG_DEBUG, "%s: processing %s %s %s for %s\n",
+		  __FUNCTION__,lim_type,lim_item,lim_value,
+		  limits_def_names[source]);
 
     if (strcmp(lim_item, "cpu") == 0)
         limit_item = RLIMIT_CPU;
@@ -310,6 +349,10 @@
 	limit_item = RLIMIT_MEMLOCK;
     else if (strcmp(lim_item, "as") == 0)
 	limit_item = RLIMIT_AS;
+#ifdef RLIMIT_LOCKS
+    else if (strcmp(lim_item, "locks") == 0)
+	limit_item = RLIMIT_LOCKS;
+#endif
     else if (strcmp(lim_item, "maxlogins") == 0) {
 	limit_item = LIMIT_LOGIN;
 	pl->flag_numsyslogins = 0;
@@ -334,7 +377,7 @@
         return;
     }
     
-    limit_value = strtol(lim_value, endptr, 10);
+    limit_value = strtol(lim_value, (char**)endptr, 10);
     if (limit_value == 0 && value_orig == *endptr) { /* no chars read */
         if (strcmp(lim_value,"-") != 0) {
             _pam_log(LOG_DEBUG,"wrong limit value '%s'", lim_value);
@@ -364,9 +407,9 @@
             break;
     }
     
-    if (limit_item != LIMIT_LOGIN && limit_item != LIMIT_NUMSYSLOGINS 
-		    && limit_item != LIMIT_PRI
-		    ) {
+    if ((limit_item != LIMIT_LOGIN) &&
+	(limit_item != LIMIT_NUMSYSLOGINS) &&
+	(limit_item != LIMIT_PRI)) {
         if (limit_type & LIMIT_SOFT) {
 	    if (pl->limits[limit_item].src_soft < source) {
                 return;
@@ -430,7 +473,8 @@
         /* skip the leading white space */
         while (*tptr && isspace(*tptr))
             tptr++;
-        strcpy(buf, (const char *)tptr);
+        strncpy(buf, (const char *)tptr, sizeof(buf) - 1);
+	buf[sizeof(buf) - 1] = '\0';
                                 
         /* Rip off the comments */
         tptr = strchr(buf,'#');
@@ -468,6 +512,7 @@
             if (strcmp(uname, domain) == 0) /* this user have a limit */
                 process_limit(LIMITS_DEF_USER, ltype, item, value, ctrl, pl);
             else if (domain[0]=='@') {
+	        _pam_log(LOG_DEBUG, "checking if %s is in group %s", uname, domain + 1);
                 if (is_on_group(uname, domain+1))
                     process_limit(LIMITS_DEF_GROUP, ltype, item, value, ctrl,
 				  pl);
@@ -529,6 +574,7 @@
     struct passwd *pwd;
     int ctrl;
     struct pam_limit_s pl;
+    char error_buf[BUFSIZ];
 
     D(("called."));
 
@@ -541,9 +587,7 @@
         return PAM_SESSION_ERR;
      }
 	
-    setpwent();
     pwd = getpwnam(user_name);
-    endpwent();
     if (!pwd) {
         if (ctrl & PAM_DEBUG_ARG)
             _pam_log(LOG_WARNING, "open_session username '%s' does not exist",
@@ -577,9 +621,17 @@
 
     if (ctrl & PAM_DO_SETREUID)
 	setreuid(pwd->pw_uid, -1);
+
     retval = setup_limits(pwd->pw_name, ctrl, &pl);
     if (retval & LOGIN_ERR) {
-        printf("\nToo many logins for '%s'\n",pwd->pw_name);
+	snprintf(error_buf, sizeof(error_buf), "Too many logins for '%s'.\n",
+		 pwd->pw_name);
+#if 0
+	if (pam_get_item(pamh, PAM_CONV, (const void **)&conv) == PAM_SUCCESS)
+	    conv->conv(1, messages, &responses, conv->appdata_ptr);
+	else
+#endif
+            printf("%s", error_buf);
         sleep(2);
         return PAM_PERM_DENIED;
     }
diff -uNr Linux-PAM-0.75/modules/pam_limits/README Linux-PAM-0.75/modules/pam_limits/README
--- Linux-PAM-0.75/modules/pam_limits/README	Wed Mar 28 23:21:16 2001
+++ Linux-PAM-0.75/modules/pam_limits/README	Tue Jul 10 00:14:23 2001
@@ -37,10 +37,11 @@
 	- as - address space limit
 	- maxlogins - max number of logins for this user
 	- maxsyslogins - max number of logins on the system
-
+	- locks - max locked files (Linux 2.4 and higher)
+		
 Note, if you specify a type of '-' but neglect to supply the item and
 value fields then the module will never enforce any limits on the
-specified user/group etc. .
+specified user/group etc.
 
 Please remember that individual limits have priority over group
 limits, so if you impose no limits for admin group, but one of the
@@ -95,4 +96,4 @@
 AUTHOR:
         Cristian Gafton <gafton@redhat.com>
 	Thanks to Elliot Lee <sopwith@redhat.com> for his comments on
-	improving this module.
+	improving this module, and Jens Sorensen for Linux 2.4 updates.
