diff --git a/ChangeLog b/ChangeLog
index d8c954d..0d2251d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2011-06-15  Thorsten Kukuk  <kukuk@thkukuk.de>
+
+	* modules/pam_limits/pam_limits.c: Add init_all option,
+	read limits from PID one if no limit is specified and init_all
+	is set.
+	* modules/pam_limits/pam_limits.8.xml: Document init_all option.
+	Based on Patch by Kees Cook.
+
 2011-06-14  Thorsten Kukuk  <kukuk@thkukuk.de>
 
 	* configure.in: Check for libtirpc by default.
diff --git a/modules/pam_limits/pam_limits.8.xml b/modules/pam_limits/pam_limits.8.xml
index 7b944f9..96fd42f 100644
--- a/modules/pam_limits/pam_limits.8.xml
+++ b/modules/pam_limits/pam_limits.8.xml
@@ -29,6 +29,9 @@
         debug
       </arg>
       <arg choice="opt">
+        init_all
+      </arg>
+      <arg choice="opt">
         utmp_early
       </arg>
       <arg choice="opt">
@@ -90,6 +93,17 @@
       </varlistentry>
       <varlistentry>
         <term>
+          <option>init_all</option>
+        </term>
+        <listitem>
+          <para>
+            Initialise all limits with the one from the process with
+            the PID 1 if no default is specified in configuration file.
+          </para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term>
           <option>utmp_early</option>
         </term>
         <listitem>
diff --git a/modules/pam_limits/pam_limits.c b/modules/pam_limits/pam_limits.c
index 8814652..971e089 100644
--- a/modules/pam_limits/pam_limits.c
+++ b/modules/pam_limits/pam_limits.c
@@ -51,9 +51,10 @@
 #define LIMITS_DEF_USER     0 /* limit was set by an user entry */
 #define LIMITS_DEF_GROUP    1 /* limit was set by a group entry */
 #define LIMITS_DEF_ALLGROUP 2 /* limit was set by a group entry */
-#define LIMITS_DEF_ALL      3 /* limit was set by an default entry */
-#define LIMITS_DEF_DEFAULT  4 /* limit was set by an default entry */
-#define LIMITS_DEF_NONE     5 /* this limit was not set yet */
+#define LIMITS_DEF_ALL      3 /* limit was set by an all entry */
+#define LIMITS_DEF_DEFAULT  4 /* limit was set by a default entry */
+#define LIMITS_DEF_KERNEL   5 /* limit was set from /proc/1/limits */
+#define LIMITS_DEF_NONE     6 /* this limit was not set yet */
 
 #define LIMIT_RANGE_ERR    -1 /* error in specified uid/gid range */
 #define LIMIT_RANGE_NONE    0 /* no range specified */
@@ -67,6 +68,7 @@ static const char *limits_def_names[] = {
        "ALLGROUP",
        "ALL",
        "DEFAULT",
+       "KERNEL",
        "NONE",
        NULL
 };
@@ -111,6 +113,7 @@ struct pam_limit_s {
 #define PAM_DEBUG_ARG       0x0001
 #define PAM_UTMP_EARLY      0x0004
 #define PAM_NO_AUDIT        0x0008
+#define PAM_INIT_ALL        0x0010
 
 /* Limits from globbed files. */
 #define LIMITS_CONF_GLOB LIMITS_FILE_DIR
@@ -136,6 +139,8 @@ _pam_parse (const pam_handle_t *pamh, int argc, const char **argv,
 	    ctrl |= PAM_UTMP_EARLY;
 	} else if (!strcmp(*argv,"noaudit")) {
 	    ctrl |= PAM_NO_AUDIT;
+	} else if (!strcmp(*argv,"init_all")) {
+	    ctrl |= PAM_INIT_ALL;
 	} else {
 	    pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv);
 	}
@@ -292,7 +297,139 @@ check_logins (pam_handle_t *pamh, const char *name, int limit, int ctrl,
     return 0;
 }
 
-static int init_limits(struct pam_limit_s *pl)
+static const char *lnames[RLIM_NLIMITS] = {
+        [RLIMIT_CPU] = "Max cpu time",
+        [RLIMIT_FSIZE] = "Max file size",
+        [RLIMIT_DATA] = "Max data size",
+        [RLIMIT_STACK] = "Max stack size",
+        [RLIMIT_CORE] = "Max core file size",
+        [RLIMIT_RSS] = "Max resident set",
+        [RLIMIT_NPROC] = "Max processes",
+        [RLIMIT_NOFILE] = "Max open files",
+        [RLIMIT_MEMLOCK] = "Max locked memory",
+#ifdef RLIMIT_AS
+        [RLIMIT_AS] = "Max address space",
+#endif
+#ifdef RLIMIT_LOCKS
+        [RLIMIT_LOCKS] = "Max file locks",
+#endif
+#ifdef RLIMIT_SIGPENDING
+        [RLIMIT_SIGPENDING] = "Max pending signals",
+#endif
+#ifdef RLIMIT_MSGQUEUE
+        [RLIMIT_MSGQUEUE] = "Max msgqueue size",
+#endif
+#ifdef RLIMIT_NICE
+        [RLIMIT_NICE] = "Max nice priority",
+#endif
+#ifdef RLIMIT_RTPRIO
+        [RLIMIT_RTPRIO] = "Max realtime priority",
+#endif
+#ifdef RLIMIT_RTTIME
+        [RLIMIT_RTTIME] = "Max realtime timeout",
+#endif
+};
+
+static int str2rlimit(char *name) {
+    int i;
+    if (!name || *name == '\0')
+        return -1;
+    for(i = 0; i < RLIM_NLIMITS; i++) {
+        if (strcmp(name, lnames[i]) == 0) return i;
+    }
+    return -1;
+}
+
+static rlim_t str2rlim_t(char *value) {
+    unsigned long long rlimit = 0;
+
+    if (!value) return (rlim_t)rlimit;
+    if (strcmp(value, "unlimited") == 0) {
+        return RLIM_INFINITY;
+    }
+    rlimit = strtoull(value, NULL, 10);
+    return (rlim_t)rlimit;
+}
+
+#define LIMITS_SKIP_WHITESPACE { \
+        /* step backwards over spaces */ \
+        pos--; \
+        while (pos && line[pos] == ' ') pos--; \
+        if (!pos) continue; \
+        line[pos+1] = '\0'; \
+}
+#define LIMITS_MARK_ITEM(item) { \
+        /* step backwards over non-spaces */ \
+        pos--; \
+        while (pos && line[pos] != ' ') pos--; \
+        if (!pos) continue; \
+        item = line + pos + 1; \
+}
+
+static void parse_kernel_limits(pam_handle_t *pamh, struct pam_limit_s *pl)
+{
+    int i, maxlen = 0;
+    FILE *limitsfile;
+    const char *proclimits = "/proc/1/limits";
+    char line[256];
+    char *units, *hard, *soft, *name;
+
+    if (!(limitsfile = fopen(proclimits, "r"))) {
+        pam_syslog(pamh, LOG_WARNING, "Could not read %s (%s), using PAM defaults", proclimits, strerror(errno));
+        return;
+    }
+
+    while (fgets(line, 256, limitsfile)) {
+        int pos = strlen(line);
+        if (pos < 2) continue;
+
+        /* drop trailing newline */
+        if (line[pos-1] == '\n') {
+            pos--;
+            line[pos] = '\0';
+        }
+
+        /* determine formatting boundry of limits report */
+        if (!maxlen && strncmp(line, "Limit", 5) == 0) {
+            maxlen = pos;
+            continue;
+        }
+
+        if (pos == maxlen) {
+            /* step backwards over "Units" name */
+            LIMITS_SKIP_WHITESPACE;
+            LIMITS_MARK_ITEM(units);
+        }
+        else {
+            units = "";
+        }
+
+        /* step backwards over "Hard Limit" value */
+        LIMITS_SKIP_WHITESPACE;
+        LIMITS_MARK_ITEM(hard);
+
+        /* step backwards over "Soft Limit" value */
+        LIMITS_SKIP_WHITESPACE;
+        LIMITS_MARK_ITEM(soft);
+
+        /* step backwards over name of limit */
+        LIMITS_SKIP_WHITESPACE;
+        name = line;
+
+        i = str2rlimit(name);
+        if (i < 0 || i >= RLIM_NLIMITS) {
+            pam_syslog(pamh, LOG_DEBUG, "Unknown kernel rlimit '%s' ignored", name);
+            continue;
+        }
+        pl->limits[i].limit.rlim_cur = str2rlim_t(soft);
+        pl->limits[i].limit.rlim_max = str2rlim_t(hard);
+        pl->limits[i].src_soft = LIMITS_DEF_KERNEL;
+        pl->limits[i].src_hard = LIMITS_DEF_KERNEL;
+    }
+    fclose(limitsfile);
+}
+
+static int init_limits(pam_handle_t *pamh, struct pam_limit_s *pl, int ctrl)
 {
     int i;
     int retval = PAM_SUCCESS;
@@ -313,6 +450,20 @@ static int init_limits(struct pam_limit_s *pl)
 	}
     }
 
+#ifdef __linux__
+    if (ctrl & PAM_INIT_ALL) {
+      parse_kernel_limits(pamh, pl);
+
+      for(i = 0; i < RLIM_NLIMITS; i++) {
+	if (pl->limits[i].supported &&
+	    (pl->limits[i].src_soft == LIMITS_DEF_NONE ||
+	     pl->limits[i].src_hard == LIMITS_DEF_NONE)) {
+	  pam_syslog(pamh, LOG_WARNING, "Did not find kernel RLIMIT for %s, using PAM default", rlimit2str(i));
+	}
+      }
+    }
+#endif
+
     errno = 0;
     pl->priority = getpriority (PRIO_PROCESS, 0);
     if (pl->priority == -1 && errno != 0)
@@ -873,7 +1024,7 @@ pam_sm_open_session (pam_handle_t *pamh, int flags UNUSED,
         return PAM_USER_UNKNOWN;
     }
 
-    retval = init_limits(pl);
+    retval = init_limits(pamh, pl, ctrl);
     if (retval != PAM_SUCCESS) {
         pam_syslog(pamh, LOG_WARNING, "cannot initialize");
         return PAM_ABORT;
