1 /* This file is part of GNU Pies.
2 Copyright (C) 2007-2020 Sergey Poznyakoff
3
4 GNU Pies is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3, or (at your option)
7 any later version.
8
9 GNU Pies is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with GNU Pies. If not, see <http://www.gnu.org/licenses/>. */
16
17 #ifdef HAVE_CONFIG_H
18 # include <config.h>
19 #endif
20 #include <stdlib.h>
21 #include <string.h>
22 #include <pwd.h>
23 #include <grp.h>
24 #include <unistd.h>
25 #include <sysexits.h>
26 #include <stdbool.h>
27 #include "pies.h"
28
29 struct grecs_list *
get_user_groups(struct grecs_list * init_list,const char * user)30 get_user_groups (struct grecs_list *init_list, const char *user)
31 {
32 int rc;
33 struct group *gr;
34 struct grecs_list *list;
35
36 list = grecs_list_create ();
37
38 if (init_list)
39 {
40 struct grecs_list_entry *ep;
41
42 for (ep = init_list->head; ep; ep = ep->next)
43 {
44 grecs_list_append (list, grecs_strdup ((char*)ep->data));
45 }
46 }
47
48 setgrent ();
49 for (rc = 0; rc == 0 && (gr = getgrent ());)
50 {
51 char **p;
52 for (p = gr->gr_mem; *p; p++)
53 if (strcmp (*p, user) == 0)
54 {
55 grecs_list_append (list, grecs_strdup (gr->gr_name));
56 break;
57 }
58 }
59 endgrent ();
60 return list;
61 }
62
63 /* Switch to the given UID/GID */
64 int
switch_to_privs(uid_t uid,gid_t gid,struct grecs_list * retain_groups)65 switch_to_privs (uid_t uid, gid_t gid, struct grecs_list *retain_groups)
66 {
67 int rc = 0;
68 gid_t *emptygidset;
69 size_t size = 1, j = 1;
70
71 /* Create a list of supplementary groups */
72 size = 1 + (retain_groups ? grecs_list_size (retain_groups) : 0);
73 emptygidset = grecs_calloc (size, sizeof emptygidset[0]);
74 emptygidset[0] = gid ? gid : getegid ();
75
76 if (retain_groups)
77 {
78 struct grecs_list_entry *ep;
79
80 for (ep = retain_groups->head; ep; ep = ep->next)
81 {
82 const char *grname = ep->data;
83 struct group *group = getgrnam (grname);
84 if (!group)
85 {
86 logmsg (LOG_ERR, _("unknown group: %s"), grname);
87 free (emptygidset);
88 return 1;
89 }
90 emptygidset[j++] = group->gr_gid;
91 }
92 }
93
94 /* Reset group permissions */
95 if (geteuid () == 0 && setgroups (j, emptygidset))
96 {
97 logmsg (LOG_ERR, _("setgroups(1, %lu) failed: %s"),
98 (unsigned long) emptygidset[0], strerror (errno));
99 rc = 1;
100 }
101 grecs_free (emptygidset);
102
103 /* Switch to the user's gid. On some OSes the effective gid must
104 be reset first */
105
106 #if defined(HAVE_SETEGID)
107 if ((rc = setegid (gid)) < 0)
108 logmsg (LOG_ERR, _("setegid(%lu) failed: %s"),
109 (unsigned long) gid, strerror (errno));
110 #elif defined(HAVE_SETREGID)
111 if ((rc = setregid (gid, gid)) < 0)
112 logmsg (LOG_ERR, _("setregid(%lu,%lu) failed: %s"),
113 (unsigned long) gid, (unsigned long) gid, strerror (errno));
114 #elif defined(HAVE_SETRESGID)
115 if ((rc = setresgid (gid, gid, gid)) < 0)
116 logmsg (LOG_ERR, _("setresgid(%lu,%lu,%lu) failed: %s"),
117 (unsigned long) gid,
118 (unsigned long) gid, (unsigned long) gid, strerror (errno));
119 #endif
120
121 if (rc == 0 && gid != 0)
122 {
123 if ((rc = setgid (gid)) < 0 && getegid () != gid)
124 logmsg (LOG_ERR, _("setgid(%lu) failed: %s"),
125 (unsigned long) gid, strerror (errno));
126 if (rc == 0 && getegid () != gid)
127 {
128 logmsg (LOG_ERR, _("cannot set effective gid to %lu"),
129 (unsigned long) gid);
130 rc = 1;
131 }
132 }
133
134 /* Now reset uid */
135 if (rc == 0 && uid != 0)
136 {
137 uid_t euid;
138
139 if (setuid (uid)
140 || geteuid () != uid
141 || (getuid () != uid && (geteuid () == 0 || getuid () == 0)))
142 {
143
144 #if defined(HAVE_SETREUID)
145 if (geteuid () != uid)
146 {
147 if (setreuid (uid, -1) < 0)
148 {
149 logmsg (LOG_ERR, _("setreuid(%lu,-1) failed: %s"),
150 (unsigned long) uid, strerror (errno));
151 rc = 1;
152 }
153 if (setuid (uid) < 0)
154 {
155 logmsg (LOG_ERR, _("second setuid(%lu) failed: %s"),
156 (unsigned long) uid, strerror (errno));
157 rc = 1;
158 }
159 }
160 else
161 #endif
162 {
163 logmsg (LOG_ERR, _("setuid(%lu) failed: %s"),
164 (unsigned long) uid, strerror (errno));
165 rc = 1;
166 }
167 }
168
169 euid = geteuid ();
170 if (uid != 0 && setuid (0) == 0)
171 {
172 logmsg (LOG_ERR, _("seteuid(0) succeeded when it should not"));
173 rc = 1;
174 }
175 else if (uid != euid && setuid (euid) == 0)
176 {
177 logmsg (LOG_ERR, _("cannot drop non-root setuid privileges"));
178 rc = 1;
179 }
180
181 }
182
183 return rc;
184 }
185
186
187 void
pies_priv_setup(struct pies_privs * privs)188 pies_priv_setup (struct pies_privs *privs)
189 {
190 struct passwd *pw;
191 struct grecs_list *grplist = 0;
192
193 if (!privs || !privs->user)
194 return;
195
196 pw = getpwnam (privs->user);
197 if (!pw)
198 {
199 logmsg (LOG_ERR, _("no such user: %s"), privs->user);
200 exit (EX_CONFIG);
201 }
202
203 if (privs->allgroups)
204 grplist = get_user_groups (privs->groups, privs->user);
205 if (switch_to_privs (pw->pw_uid, pw->pw_gid,
206 grplist ? grplist : privs->groups))
207 exit (EX_SOFTWARE);
208 if (grplist)
209 grecs_list_free (grplist);
210 }
211
212
213 void
pies_epriv_setup(struct pies_privs * privs)214 pies_epriv_setup (struct pies_privs *privs)
215 {
216 uid_t uid;
217 gid_t gid;
218
219 if (privs)
220 {
221 struct passwd *pw;
222 if (!privs->user)
223 return;
224
225 pw = getpwnam (privs->user);
226 if (!pw)
227 {
228 logmsg (LOG_ERR, _("no such user: %s"), privs->user);
229 exit (EX_CONFIG);
230 }
231 uid = pw->pw_uid;
232 gid = pw->pw_gid;
233 }
234 else
235 {
236 uid = 0;
237 gid = 0;
238 }
239
240 if (setegid (gid))
241 {
242 logmsg (LOG_ERR, _("cannot switch to EGID %lu: %s"),
243 (unsigned long) gid, strerror (errno));
244 exit (EX_USAGE);
245 }
246 if (seteuid (uid))
247 {
248 logmsg (LOG_ERR, _("cannot switch to EUID %lu: %s"),
249 (unsigned long) uid, strerror (errno));
250 exit (EX_USAGE);
251 }
252 }
253
254 int
pies_privs_cmp(struct pies_privs const * a,struct pies_privs const * b)255 pies_privs_cmp (struct pies_privs const *a, struct pies_privs const *b)
256 {
257 if (!a)
258 return !!b;
259 else if (!b)
260 return 1;
261 if (safe_strcmp (a->user, b->user))
262 return 1;
263 if (a->allgroups != b->allgroups)
264 return 1;
265 return grecs_list_compare (a->groups, b->groups);
266 }
267
268 void
pies_privs_free(struct pies_privs * p)269 pies_privs_free (struct pies_privs *p)
270 {
271 grecs_free (p->user);
272 grecs_list_free (p->groups);
273 }
274
275
276