xref: /onnv-gate/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/privs.c (revision 9799:641e52717cb5)
1 /*
2  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*
7  * Least privilege support functions.
8  */
9 
10 #include "config.h"
11 
12 #ifdef SOLARIS_PRIVS
13 #include <priv.h>
14 #ifdef HAVE_SYS_SYSLOG_H
15 #include <sys/syslog.h>
16 #endif
17 #if defined(HAVE_SYSLOG_H) || (!defined(AUTOCONF) && !defined(HAVE_SYS_SYSLOG_H))
18 #include <syslog.h>
19 #endif
20 #endif /* SOLARIS_PRIVS */
21 
22 #include "proto.h"
23 
24 #ifdef SOLARIS_PRIVS
25 /* When ununitialized, this indicates we still have all privs */
26 static priv_set_t *uprivs;
27 #endif /* SOLARIS_PRIVS */
28 
29 #ifdef SOLARIS_PRIVS
30 #ifdef PRIVS_DEBUG
print_privs(priv_ptype_t which,const char * str)31 static void print_privs(priv_ptype_t which, const char *str)
32 {
33     priv_set_t *privset;
34     char *privstr;
35 
36     if ((privset = priv_allocset()) == NULL)
37 	return;
38 
39     (void) getppriv(which, privset);
40     privstr = priv_set_to_str(privset, ',', PRIV_STR_SHORT);
41     syslog(LOG_DEBUG, "%s: %s", str, privstr);
42     free(privstr);
43     priv_freeset(privset);
44 }
45 #endif /* PRIVS_DEBUG */
46 
priv_on(const char * priv)47 static void priv_on(const char *priv)
48 {
49     /* no need to add the privilege if already have it */
50     if (uprivs == NULL || priv_ismember(uprivs, priv))
51 	return;
52 
53     if (priv_set(PRIV_ON, PRIV_EFFECTIVE, priv, NULL) == -1)
54 	syslog(LOG_ERR, "priv_set: error adding privilege %s: %m", priv);
55 }
56 
priv_off(const char * priv)57 static void priv_off(const char *priv)
58 {
59     /* don't remove the privilege if already had it */
60     if (uprivs == NULL || priv_ismember(uprivs, priv))
61 	return;
62 
63     if (priv_set(PRIV_OFF, PRIV_EFFECTIVE, priv, NULL) == -1)
64 	syslog(LOG_ERR, "priv_set: error removing privilege %s: %m", priv);
65 }
66 #endif /* SOLARIS_PRIVS */
67 
68 /*
69  * init_privs() is called after a user has logged in to drop from the
70  * permitted privilege set those privileges which are no longer required.
71  */
72 /*ARGSUSED*/
init_privs(const char * username)73 void init_privs(const char *username)
74 {
75 #ifdef SOLARIS_PRIVS
76     uid_t euid = geteuid();
77     priv_set_t *pset1, *pset2;
78 
79     /*
80      * The FTP server runs with the inheritable set and the limit set
81      * filled in through user_attr (or with default values of basic and all).
82      * The privileges available to the user at login, is an intersection
83      * of both those sets.  The only way to limit the root user is by
84      * changing the limit set, not by changing the I set.
85      */
86     if ((pset1 = priv_allocset()) == NULL ||
87 	(uprivs = priv_allocset()) == NULL ||
88 	(pset2 = priv_allocset()) == NULL) {
89 	    syslog(LOG_ERR, "priv_allocset failed: %m");
90 	    dologout(1);
91     }
92     if (getppriv(PRIV_LIMIT, pset1) == -1) {
93 	syslog(LOG_ERR, "getppriv(limit) failed: %m");
94 	dologout(1);
95     }
96     if (getppriv(euid == 0 ? PRIV_PERMITTED : PRIV_INHERITABLE, pset2) == -1) {
97 	syslog(LOG_ERR, "getppriv() failed: %m");
98 	dologout(1);
99     }
100 
101     /* Compute the permitted set after login. */
102     priv_intersect(pset2, pset1);
103 
104     /*
105      * Set the permitted privilege set to the allowable privileges plus
106      * those required after init_privs() is called. Keep note of which
107      * effective privileges we already had in uprivs so we don't turn
108      * them off.
109      */
110     priv_emptyset(pset2);
111     (void) priv_addset(pset2, PRIV_PROC_SETID);
112     (void) priv_addset(pset2, PRIV_NET_PRIVADDR);
113     (void) priv_addset(pset2, PRIV_FILE_DAC_READ);
114     (void) priv_addset(pset2, PRIV_FILE_DAC_SEARCH);
115     (void) priv_addset(pset2, PRIV_FILE_CHOWN);
116 
117     priv_copyset(pset2, uprivs);
118     priv_intersect(pset1, uprivs);
119 
120     /* Now, set the effective privileges. */
121     if (setppriv(PRIV_SET, PRIV_EFFECTIVE, pset1) == -1) {
122 	syslog(LOG_ERR,
123 	    "unable to set privileges for %s: setppriv(effective): %m",
124 	    username);
125 	dologout(1);
126     }
127 
128 #if defined(SOLARIS_BSM_AUDIT) && !defined(SOLARIS_NO_AUDIT_FTPD_LOGOUT)
129     /* needed for audit_ftpd_logout() */
130     (void) priv_addset(pset1, PRIV_PROC_AUDIT);
131 #endif
132     /* And set the permitted, adding ftpd's required privileges in the mix. */
133     priv_union(pset2, pset1);
134     if (setppriv(PRIV_SET, PRIV_PERMITTED, pset1) == -1) {
135 	syslog(LOG_ERR,
136 	    "unable to set privileges for %s: setppriv(permitted): %m",
137 	    username);
138 	dologout(1);
139     }
140     /*
141      * setppriv() has made us privilege aware, so the effective privileges
142      * are no longer modified by user ID changes.
143      */
144     priv_freeset(pset1);
145     priv_freeset(pset2);
146 
147     /* set the real, effective and saved group ID's */
148     setid_priv_on(0);
149     if (setgid(getegid()) != 0) {
150 	syslog(LOG_ERR, "setgid(%d) failed: %m", getegid());
151 	setid_priv_off(euid);
152 	dologout(1);
153     }
154     /*
155      * Set the real and effective user ID's, leaving the saved user ID set
156      * to 0 so seteuid(0) succeeds.
157      */
158     (void) seteuid(0);
159     if (setreuid(euid, -1) != 0) {
160 	syslog(LOG_ERR, "setreuid(%d, -1) failed: %m", euid);
161 	setid_priv_off(euid);
162 	dologout(1);
163     }
164     setid_priv_off(euid);
165     if (seteuid(euid) != 0) {
166 	syslog(LOG_ERR, "seteuid(%d) failed: %m", euid);
167 	dologout(1);
168     }
169 
170 #ifdef PRIVS_DEBUG
171     print_privs(PRIV_EFFECTIVE, "effective privilege set");
172     print_privs(PRIV_PERMITTED, "permitted privilege set");
173     print_privs(PRIV_INHERITABLE, "inheritable privilege set");
174     print_privs(PRIV_LIMIT, "limit privilege set");
175 #endif /* PRIVS_DEBUG */
176 #endif /* SOLARIS_PRIVS */
177 }
178 
179 /* allow a process to bind to a privileged port */
180 /*ARGSUSED*/
port_priv_on(uid_t uid)181 void port_priv_on(uid_t uid)
182 {
183     delay_signaling();
184 #ifdef SOLARIS_PRIVS
185     priv_on(PRIV_NET_PRIVADDR);
186 #else
187     (void) seteuid(uid);
188 #endif
189 }
190 
191 /*ARGSUSED*/
port_priv_off(uid_t uid)192 void port_priv_off(uid_t uid)
193 {
194 #ifdef SOLARIS_PRIVS
195     priv_off(PRIV_NET_PRIVADDR);
196 #else
197     (void) seteuid(uid);
198 #endif
199     enable_signaling();
200 }
201 
202 /* allow a process to read any file or directory and to search any directory */
access_priv_on(uid_t uid)203 void access_priv_on(uid_t uid)
204 {
205     delay_signaling();
206 #ifdef SOLARIS_PRIVS
207     priv_on(PRIV_FILE_DAC_READ);
208     priv_on(PRIV_FILE_DAC_SEARCH);
209 #endif
210     /* necessary on Solaris for access over NFS */
211     (void) seteuid(uid);
212 }
213 
access_priv_off(uid_t uid)214 void access_priv_off(uid_t uid)
215 {
216 #ifdef SOLARIS_PRIVS
217     priv_off(PRIV_FILE_DAC_READ);
218     priv_off(PRIV_FILE_DAC_SEARCH);
219 #endif
220     (void) seteuid(uid);
221     enable_signaling();
222 }
223 
224 /* allow a process to set its user IDs and group IDs */
225 /*ARGSUSED*/
setid_priv_on(uid_t uid)226 void setid_priv_on(uid_t uid)
227 {
228     delay_signaling();
229 #ifdef SOLARIS_PRIVS
230     priv_on(PRIV_PROC_SETID);
231 #else
232     (void) seteuid(uid);
233 #endif
234 }
235 
236 /*ARGSUSED*/
setid_priv_off(uid_t uid)237 void setid_priv_off(uid_t uid)
238 {
239 #ifdef SOLARIS_PRIVS
240     priv_off(PRIV_PROC_SETID);
241 #else
242     (void) seteuid(uid);
243 #endif
244     enable_signaling();
245 }
246 
247 /* allow a process to change the ownership of files and directories */
chown_priv_on(uid_t uid)248 void chown_priv_on(uid_t uid)
249 {
250     delay_signaling();
251 #ifdef SOLARIS_PRIVS
252     priv_on(PRIV_FILE_CHOWN);
253 #endif
254     /* necessary on Solaris for chown over NFS */
255     (void) seteuid(uid);
256 }
257 
chown_priv_off(uid_t uid)258 void chown_priv_off(uid_t uid)
259 {
260 #ifdef SOLARIS_PRIVS
261     priv_off(PRIV_FILE_CHOWN);
262 #endif
263     (void) seteuid(uid);
264     enable_signaling();
265 }
266