1/*
2 * Copyright (c) 2008, 2010 Todd C. Miller <Todd.Miller@courtesan.com>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17#include <config.h>
18
19#include <sys/types.h>
20#include <sys/resource.h>
21
22#include <stdio.h>
23#ifdef STDC_HEADERS
24# include <stdlib.h>
25# include <stddef.h>
26#else
27# ifdef HAVE_STDLIB_H
28#  include <stdlib.h>
29# endif
30#endif /* STDC_HEADERS */
31#include <usersec.h>
32#include <uinfo.h>
33
34#include "missing.h"
35#include "alloc.h"
36#include "error.h"
37
38#ifdef HAVE_GETUSERATTR
39
40#ifndef HAVE_SETRLIMIT64
41# define setrlimit64(a, b) setrlimit(a, b)
42# define rlimit64 rlimit
43# define rlim64_t rlim_t
44# define RLIM64_INFINITY RLIM_INFINITY
45#endif /* HAVE_SETRLIMIT64 */
46
47#ifndef RLIM_SAVED_MAX
48# define RLIM_SAVED_MAX	RLIM64_INFINITY
49#endif
50
51struct aix_limit {
52    int resource;
53    char *soft;
54    char *hard;
55    int factor;
56};
57
58static struct aix_limit aix_limits[] = {
59    { RLIMIT_FSIZE, S_UFSIZE, S_UFSIZE_HARD, 512 },
60    { RLIMIT_CPU, S_UCPU, S_UCPU_HARD, 1 },
61    { RLIMIT_DATA, S_UDATA, S_UDATA_HARD, 512 },
62    { RLIMIT_STACK, S_USTACK, S_USTACK_HARD, 512 },
63    { RLIMIT_RSS, S_URSS, S_URSS_HARD, 512 },
64    { RLIMIT_CORE, S_UCORE, S_UCORE_HARD, 512 },
65    { RLIMIT_NOFILE, S_UNOFILE, S_UNOFILE_HARD, 1 }
66};
67
68static int
69aix_getlimit(user, lim, valp)
70    char *user;
71    char *lim;
72    rlim64_t *valp;
73{
74    int val;
75
76    if (getuserattr(user, lim, &val, SEC_INT) != 0)
77	return -1;
78    *valp = val;
79    return 0;
80}
81
82static void
83aix_setlimits(user)
84    char *user;
85{
86    struct rlimit64 rlim;
87    rlim64_t val;
88    int n;
89
90    if (setuserdb(S_READ) != 0)
91	error(1, "unable to open userdb");
92
93    /*
94     * For each resource limit, get the soft/hard values for the user
95     * and set those values via setrlimit64().  Must be run as euid 0.
96     */
97    for (n = 0; n < sizeof(aix_limits) / sizeof(aix_limits[0]); n++) {
98	/*
99	 * We have two strategies, depending on whether or not the
100	 * hard limit has been defined.
101	 */
102	if (aix_getlimit(user, aix_limits[n].hard, &val) == 0) {
103	    rlim.rlim_max = val == -1 ? RLIM64_INFINITY : val * aix_limits[n].factor;
104	    if (aix_getlimit(user, aix_limits[n].soft, &val) == 0)
105		rlim.rlim_cur = val == -1 ? RLIM64_INFINITY : val * aix_limits[n].factor;
106	    else
107		rlim.rlim_cur = rlim.rlim_max;	/* soft not specd, use hard */
108	} else {
109	    /* No hard limit set, try soft limit, if it exists. */
110	    if (aix_getlimit(user, aix_limits[n].soft, &val) == -1)
111		continue;
112	    rlim.rlim_cur = val == -1 ? RLIM64_INFINITY : val * aix_limits[n].factor;
113
114	    /* Set hard limit per AIX /etc/security/limits documentation. */
115	    switch (aix_limits[n].resource) {
116		case RLIMIT_CPU:
117		case RLIMIT_FSIZE:
118		    rlim.rlim_max = rlim.rlim_cur;
119		    break;
120		case RLIMIT_STACK:
121		    rlim.rlim_max = RLIM_SAVED_MAX;
122		    break;
123		default:
124		    rlim.rlim_max = RLIM64_INFINITY;
125		    break;
126	    }
127	}
128	(void)setrlimit64(aix_limits[n].resource, &rlim);
129    }
130    enduserdb();
131}
132
133#ifdef HAVE_SETAUTHDB
134/*
135 * Look up administrative domain for user (SYSTEM in /etc/security/user) and
136 * set it as the default for the process.  This ensures that password and
137 * group lookups are made against the correct source (files, NIS, LDAP, etc).
138 */
139void
140aix_setauthdb(user)
141    char *user;
142{
143    char *registry;
144
145    if (user != NULL) {
146	if (setuserdb(S_READ) != 0)
147	    error(1, "unable to open userdb");
148	if (getuserattr(user, S_REGISTRY, &registry, SEC_CHAR) == 0) {
149	    if (setauthdb(registry, NULL) != 0)
150		error(1, "unable to switch to registry \"%s\" for %s",
151		    registry, user);
152	}
153	enduserdb();
154    }
155}
156
157/*
158 * Restore the saved administrative domain, if any.
159 */
160void
161aix_restoreauthdb()
162{
163    if (setauthdb(NULL, NULL) != 0)
164	error(1, "unable to restore registry");
165}
166#endif
167
168void
169aix_prep_user(user, tty)
170    char *user;
171    char *tty;
172{
173    char *info;
174    int len;
175
176    /* set usrinfo, like login(1) does */
177    len = easprintf(&info, "NAME=%s%cLOGIN=%s%cLOGNAME=%s%cTTY=%s%c",
178	user, '\0', user, '\0', user, '\0', tty ? tty : "", '\0');
179    (void)usrinfo(SETUINFO, info, len);
180    efree(info);
181
182#ifdef HAVE_SETAUTHDB
183    /* set administrative domain */
184    aix_setauthdb(user);
185#endif
186
187    /* set resource limits */
188    aix_setlimits(user);
189}
190#endif /* HAVE_GETUSERATTR */
191