Hi! I wrote a small patch to enable /etc/limits support in openssh. nice thing when you don't have PAM installed.. It is based on Ultor's openssh 1.x patch (http://marc.theaimsgroup.com/?l=secure-shell&m=96427677022741&w=2) Works fine on slackware7.1. define USE_ETC_LIMITS in config.h , and compile as usual. Sagi -------------- next part -------------- diff -N -u openssh-2.3.0p1.orig/Makefile.in openssh-2.3.0p1/Makefile.in --- openssh-2.3.0p1.orig/Makefile.in Wed Feb 7 22:24:35 2001 +++ openssh-2.3.0p1/Makefile.in Wed Feb 7 22:43:55 2001 @@ -41,7 +41,7 @@ SSHOBJS= ssh.o sshconnect.o sshconnect1.o sshconnect2.o log-client.o readconf.o clientloop.o -SSHDOBJS= sshd.o auth.o auth1.o auth2.o auth-skey.o auth2-skey.o auth-rhosts.o auth-options.o auth-krb4.o auth-pam.o auth-passwd.o auth-rsa.o auth-rh-rsa.o dh.o pty.o log-server.o login.o loginrec.o servconf.o serverloop.o md5crypt.o session.o +SSHDOBJS= sshd.o auth.o auth1.o auth2.o auth-skey.o auth2-skey.o auth-rhosts.o auth-options.o auth-krb4.o auth-pam.o auth-passwd.o auth-rsa.o auth-rh-rsa.o dh.o pty.o log-server.o login.o loginrec.o servconf.o serverloop.o md5crypt.o session.o limits.o TROFFMAN = scp.1 ssh-add.1 ssh-agent.1 ssh-keygen.1 ssh.1 sshd.8 sftp-server.8 CATMAN = scp.0 ssh-add.0 ssh-agent.0 ssh-keygen.0 ssh.0 sshd.0 sftp-server.0 diff -N -u openssh-2.3.0p1.orig/config.h.in openssh-2.3.0p1/config.h.in --- openssh-2.3.0p1.orig/config.h.in Wed Feb 7 22:24:36 2001 +++ openssh-2.3.0p1/config.h.in Wed Feb 7 22:43:55 2001 @@ -5,6 +5,8 @@ /* Generated automatically from acconfig.h by autoheader. */ /* Please make your changes there */ +/* use /etc/limits*/ +#undef USE_ETC_LIMITS /* Define if the `getpgrp' function takes no argument. */ #undef GETPGRP_VOID Common subdirectories: openssh-2.3.0p1.orig/contrib and openssh-2.3.0p1/contrib diff -N -u openssh-2.3.0p1.orig/limits.c openssh-2.3.0p1/limits.c --- openssh-2.3.0p1.orig/limits.c Thu Jan 1 02:00:00 1970 +++ openssh-2.3.0p1/limits.c Wed Feb 7 22:43:55 2001 @@ -0,0 +1,340 @@ +/* + * Copyright 1989 - 1994, Julianne Frances Haugh + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Julianne F. Haugh nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Separated from setup.c. --marekm + * Resource limits thanks to Cristian Gafton. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <syslog.h> + +#include <stdio.h> +#include <utmp.h> +#include <pwd.h> + +#include <sys/resource.h> +#define LIMITS + +#ifdef LIMITS + +#ifndef LIMITS_FILE +#define LIMITS_FILE "/etc/limits" +#endif + +#define memzero(ptr, size) memset((void *)(ptr), 0, (size)) + +#define LOGIN_ERROR_RLIMIT 1 +#define LOGIN_ERROR_LOGIN 2 + +/* Set a limit on a resource */ +/* + * rlimit - RLIMIT_XXXX + * value - string value to be read + * multiplier - value*multiplier is the actual limit + */ +static int +setrlimit_value(unsigned int rlimit, const char *value, unsigned int multiplier) +{ + struct rlimit rlim; + long limit; + char **endptr = (char **) &value; + const char *value_orig = value; + + limit = strtol(value, endptr, 10); + if (limit == 0 && value_orig == *endptr) /* no chars read */ + return 0; + limit *= multiplier; + rlim.rlim_cur = limit; + rlim.rlim_max = limit; + if (setrlimit(rlimit, &rlim)) + return LOGIN_ERROR_RLIMIT; + return 0; +} + + +static int +set_prio(const char *value) +{ + int prio; + char **endptr = (char **) &value; + + prio = strtol(value, endptr, 10); + if ((prio == 0) && (value == *endptr)) + return 0; + if (setpriority(PRIO_PROCESS, 0, prio)) + return LOGIN_ERROR_RLIMIT; + return 0; +} + + +/* Counts the number of user logins and check against the limit */ +static int +check_logins(const char *name, const char *maxlogins) +{ + struct utmp *ut; + unsigned int limit, count; + char **endptr = (char **) &maxlogins; + const char *ml_orig = maxlogins; + + limit = strtol(maxlogins, endptr, 10); + if (limit == 0 && ml_orig == *endptr) /* no chars read */ + return 0; + + if (limit == 0) /* maximum 0 logins ? */ { + syslog(LOG_WARNING, "No logins allowed for `%s'\n", name); + return LOGIN_ERROR_LOGIN; + } + + setutent(); + count = 0; + while ((ut = getutent())) { +// if (ut->ut_type != USER_PROCESS) +// continue; + if (ut->ut_user[0] == '\0') + continue; + if (strncmp(name, ut->ut_user, sizeof(ut->ut_user)) != 0) + continue; + if (++count > limit) + break; + } + endutent(); + /* + * This is called after setutmp(), so the number of logins counted + * includes the user who is currently trying to log in. + */ + + if (count > limit) { + printf("Too many logins (max %d).\n",count); + syslog(LOG_WARNING, "Too many logins (max %d) for %s\n",limit, name); + return LOGIN_ERROR_LOGIN; + } + + return 0; +} + +/* Function setup_user_limits - checks/set limits for the curent login + * Original idea from Joel Katz's lshell. Ported to shadow-login + * by Cristian Gafton - gafton at sorosis.ro + * + * We are passed a string of the form ('BASH' constants for ulimit) + * [Aa][Cc][Dd][Ff][Mm][Nn][Rr][Ss][Tt][Uu][Ll][Pp] + * (eg. 'C2F256D2048N5' or 'C2 F256 D2048 N5') + * where: + * [Aa]: a = RLIMIT_AS max address space (KB) + * [Cc]: c = RLIMIT_CORE max core file size (KB) + * [Dd]: d = RLIMIT_DATA max data size (KB) + * [Ff]: f = RLIMIT_FSIZE Maximum filesize (KB) + * [Mm]: m = RLIMIT_MEMLOCK max locked-in-memory address space (KB) + * [Nn]: n = RLIMIT_NOFILE max number of open files + * [Rr]: r = RLIMIT_RSS max resident set size (KB) + * [Ss]: s = RLIMIT_STACK max stack size (KB) + * [Tt]: t = RLIMIT_CPU max CPU time (MIN) + * [Uu]: u = RLIMIT_NPROC max number of processes + * [Ll]: l = max number of logins for this user + * [Pp]: p = process priority -20..20 (negative = high priority) + * + * Return value: + * 0 = okay, of course + * LOGIN_ERROR_RLIMIT = error setting some RLIMIT + * LOGIN_ERROR_LOGIN = error - too many logins for this user + * + * buf - the limits string + * name - the username + */ +static int +do_user_limits(const char *buf, const char *name) +{ + const char *pp; + int retval = 0; + + pp = buf; + + while (*pp != '\0') switch(*pp++) { +#ifdef RLIMIT_AS + case 'a': + case 'A': + /* RLIMIT_AS - max address space (KB) */ + retval |= setrlimit_value(RLIMIT_AS, pp, 1024); +#endif +#ifdef RLIMIT_CPU + case 't': + case 'T': + /* RLIMIT_CPU - max CPU time (MIN) */ + retval |= setrlimit_value(RLIMIT_CPU, pp, 60); + break; +#endif +#ifdef RLIMIT_DATA + case 'd': + case 'D': + /* RLIMIT_DATA - max data size (KB) */ + retval |= setrlimit_value(RLIMIT_DATA, pp, 1024); + break; +#endif +#ifdef RLIMIT_FSIZE + case 'f': + case 'F': + /* RLIMIT_FSIZE - Maximum filesize (KB) */ + retval |= setrlimit_value(RLIMIT_FSIZE, pp, 1024); + break; +#endif +#ifdef RLIMIT_NPROC + case 'u': + case 'U': + /* RLIMIT_NPROC - max number of processes */ + retval |= setrlimit_value(RLIMIT_NPROC, pp, 1); + break; +#endif +#ifdef RLIMIT_CORE + case 'c': + case 'C': + /* RLIMIT_CORE - max core file size (KB) */ + retval |= setrlimit_value(RLIMIT_CORE, pp, 1024); + break; +#endif +#ifdef RLIMIT_MEMLOCK + case 'm': + case 'M': + /* RLIMIT_MEMLOCK - max locked-in-memory address space (KB) */ + retval |= setrlimit_value(RLIMIT_MEMLOCK, pp, 1024); + break; +#endif +#ifdef RLIMIT_NOFILE + case 'n': + case 'N': + /* RLIMIT_NOFILE - max number of open files */ + retval |= setrlimit_value(RLIMIT_NOFILE, pp, 1); + break; +#endif +#ifdef RLIMIT_RSS + case 'r': + case 'R': + /* RLIMIT_RSS - max resident set size (KB) */ + retval |= setrlimit_value(RLIMIT_RSS, pp, 1024); + break; +#endif +#ifdef RLIMIT_STACK + case 's': + case 'S': + /* RLIMIT_STACK - max stack size (KB) */ + retval |= setrlimit_value(RLIMIT_STACK, pp, 1024); + break; +#endif + case 'l': + case 'L': + /* LIMIT the number of concurent logins */ + retval |= check_logins(name, pp); + break; + case 'p': + case 'P': + retval |= set_prio(pp); + break; + } + return retval; +} + +static int +setup_user_limits(const char *uname) +{ + /* TODO: allow and use @group syntax --cristiang */ + FILE *fil; + char buf[1024]; + char name[1024]; + char limits[1024]; + char deflimits[1024]; + char tempbuf[1024]; + + /* init things */ + memzero(buf, sizeof(buf)); + memzero(name, sizeof(name)); + memzero(limits, sizeof(limits)); + memzero(deflimits, sizeof(deflimits)); + memzero(tempbuf, sizeof(tempbuf)); + + /* start the checks */ + fil = fopen(LIMITS_FILE, "r"); + if (fil == NULL) { +#if 0 /* no limits file is ok, not everyone is a BOFH :-). --marekm */ + syslog(LOG_WARNING, NO_LIMITS, uname, LIMITS_FILE); +#endif + return 0; + } + /* The limits file have the following format: + * - '#' (comment) chars only as first chars on a line; + * - username must start on first column + * A better (smarter) checking should be done --cristiang */ + while (fgets(buf, 1024, fil) != NULL) { + if (buf[0]=='#' || buf[0]=='\n') + continue; + memzero(tempbuf, sizeof(tempbuf)); + /* a valid line should have a username, then spaces, + * then limits + * we allow the format: + * username L2 D2048 R4096 + * where spaces={' ',\t}. Also, we reject invalid limits. + * Imposing a limit should be done with care, so a wrong + * entry means no care anyway :-). A '-' as a limits + * strings means no limits --cristiang */ + if (sscanf(buf, "%s%[ACDFMNRSTULPacdfmnrstulp0-9 \t-]", + name, tempbuf) == 2) { + if (strcmp(name, uname) == 0) { + strcpy(limits, tempbuf); + break; + } else if (strcmp(name, "*") == 0) { + strcpy(deflimits, tempbuf); + } + } + } + fclose(fil); + if (limits[0] == '\0') { + /* no user specific limits */ + if (deflimits[0] == '\0') /* no default limits */ + return 0; + strcpy(limits, deflimits); /* use the default limits */ + } + return do_user_limits(limits, uname); +} + +#endif /* LIMITS */ + +/* + * set the process nice, ulimit, and umask from the password file entry + */ + +void setup_limits(const struct passwd *info) { + char *cp; + int i; + long l; + + if (info->pw_uid) if (setup_user_limits(info->pw_name) & LOGIN_ERROR_LOGIN) { + sleep(2); + exit(1); + } +} diff -N -u openssh-2.3.0p1.orig/session.c openssh-2.3.0p1/session.c --- openssh-2.3.0p1.orig/session.c Wed Feb 7 22:24:35 2001 +++ openssh-2.3.0p1/session.c Wed Feb 7 22:43:55 2001 @@ -599,6 +599,10 @@ do_pam_session(pw->pw_name, s->tty); do_pam_setcred(); #endif /* USE_PAM */ + +#ifdef USE_ETC_LIMITS + setup_limits(pw); +#endif /* USE_ETC_LIMITS */ /* Fork the child. */ if ((pid = fork()) == 0) {