blob: 66681dabb16939e58e5839ebb4c654ccbcfe163b [file] [log] [blame]
paul01245822003-05-20 01:22:17 +00001/*
2 * Zebra privileges.
3 *
4 * Copyright (C) 2003 Paul Jakma.
5 *
6 * This file is part of GNU Zebra.
7 *
8 * GNU Zebra is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2, or (at your option) any
11 * later version.
12 *
13 * GNU Zebra is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with GNU Zebra; see the file COPYING. If not, write to the Free
20 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
21 * 02111-1307, USA.
22 */
23
24#include <zebra.h>
25#include "log.h"
26#include "privs.h"
27#include "memory.h"
paul01245822003-05-20 01:22:17 +000028
29/* internal privileges state */
30static struct _zprivs_t
31{
32#ifdef HAVE_LCAPS
33 cap_t caps; /* caps storage */
34 cap_value_t *syscaps_p; /* system permitted caps */
35 cap_value_t *syscaps_i; /* system inheritable caps */
36 int sys_num_p; /* number of syscaps_p */
37 int sys_num_i; /* number of syscaps_i */
38#endif /* HAVE_LCAPS */
39 uid_t zuid, /* uid to run as */
40 zsuid; /* saved uid */
41 gid_t zgid; /* gid to run as */
hassoba3a0bc2003-06-04 17:41:54 +000042 gid_t vtygrp; /* gid for vty sockets */
paul01245822003-05-20 01:22:17 +000043} zprivs_state;
44
45/* externally exported but not directly accessed functions */
46#ifdef HAVE_LCAPS
47int zprivs_change_caps (zebra_privs_ops_t);
48zebra_privs_current_t zprivs_state_caps (void);
49#endif /* HAVE_LCAPS */
50int zprivs_change_uid (zebra_privs_ops_t);
51zebra_privs_current_t zprivs_state_uid (void);
52int zprivs_change_null (zebra_privs_ops_t);
53zebra_privs_current_t zprivs_state_null (void);
54void zprivs_terminate (void);
55
56#ifdef HAVE_LCAPS
57static int
58cap_map [ZCAP_MAX] =
59{
60 [ZCAP_SETGID] = CAP_SETGID,
61 [ZCAP_SETUID] = CAP_SETUID,
62 [ZCAP_BIND] = CAP_NET_BIND_SERVICE,
63 [ZCAP_BROADCAST] = CAP_NET_BROADCAST,
64 [ZCAP_ADMIN] = CAP_NET_ADMIN,
65 [ZCAP_RAW] = CAP_NET_RAW,
66 [ZCAP_CHROOT] = CAP_SYS_CHROOT,
67 [ZCAP_NICE] = CAP_SYS_NICE,
paul8d6b00e2003-05-22 02:35:52 +000068 [ZCAP_PTRACE] = CAP_SYS_PTRACE,
69 [ZCAP_DAC_OVERRIDE] = CAP_DAC_OVERRIDE,
70 [ZCAP_READ_SEARCH] = CAP_DAC_READ_SEARCH,
71 [ZCAP_SYS_ADMIN] = CAP_SYS_ADMIN,
hassoba3a0bc2003-06-04 17:41:54 +000072 [ZCAP_FOWNER] = CAP_FOWNER
paul01245822003-05-20 01:22:17 +000073};
74
paul01245822003-05-20 01:22:17 +000075/* convert zebras privileges to system capabilities */
76static cap_value_t *
77zcaps2sys (zebra_capabilities_t *zcaps, int num)
78{
79 cap_value_t *syscaps;
80 int i;
81
82 if (!num)
83 return NULL;
84
85 syscaps = (cap_value_t *) XCALLOC ( MTYPE_PRIVS,
86 (sizeof(cap_value_t) * num) );
87 if (!syscaps)
88 {
89 zlog_err ("zcap2sys: could not XCALLOC!");
90 return NULL;
91 }
92
93 for (i=0; i < num; i++)
94 {
95 syscaps[i] = cap_map[zcaps[i]];
96 }
97
98 return syscaps;
99}
100
101/* set or clear the effective capabilities to/from permitted */
102int
103zprivs_change_caps (zebra_privs_ops_t op)
104{
105 cap_flag_value_t cflag;
106
107 if (op == ZPRIVS_RAISE)
108 cflag = CAP_SET;
109 else if (op == ZPRIVS_LOWER)
110 cflag = CAP_CLEAR;
111 else
112 return -1;
113
114 if ( !cap_set_flag (zprivs_state.caps, CAP_EFFECTIVE,
115 zprivs_state.sys_num_p, zprivs_state.syscaps_p, cflag))
116 return cap_set_proc (zprivs_state.caps);
117 return -1;
118}
119
120zebra_privs_current_t
121zprivs_state_caps (void)
122{
123 int i;
paul01245822003-05-20 01:22:17 +0000124 cap_flag_value_t val;
125
paul33b72942003-05-20 02:22:42 +0000126 for (i=0; i < zprivs_state.sys_num_p; i++)
paul01245822003-05-20 01:22:17 +0000127 {
128 if ( cap_get_flag (zprivs_state.caps, zprivs_state.syscaps_p[i],
129 CAP_EFFECTIVE, &val) )
130 zlog_warn ("zprivs_state_caps: could not cap_get_flag, %s",
ajs6099b3b2004-11-20 02:06:59 +0000131 safe_strerror (errno) );
paul01245822003-05-20 01:22:17 +0000132 if (val == CAP_SET)
paul33b72942003-05-20 02:22:42 +0000133 return ZPRIVS_RAISED;
paul01245822003-05-20 01:22:17 +0000134 }
135 return ZPRIVS_LOWERED;
136}
137
138#endif /* HAVE_LCAPS */
139
140int
141zprivs_change_uid (zebra_privs_ops_t op)
142{
paul28efaa32003-05-20 03:49:43 +0000143
paul01245822003-05-20 01:22:17 +0000144 if (op == ZPRIVS_RAISE)
145 return seteuid (zprivs_state.zsuid);
146 else if (op == ZPRIVS_LOWER)
147 return seteuid (zprivs_state.zuid);
148 else
149 return -1;
150}
151
152zebra_privs_current_t
153zprivs_state_uid (void)
154{
155 return ( (zprivs_state.zuid == geteuid()) ? ZPRIVS_LOWERED : ZPRIVS_RAISED);
156}
157
158int
159zprivs_change_null (zebra_privs_ops_t op)
160{
161 return 0;
162}
163
164zebra_privs_current_t
165zprivs_state_null (void)
166{
167 return ZPRIVS_RAISED;
168}
169
170
171void
172zprivs_init(struct zebra_privs_t *zprivs)
173{
174 struct passwd *pwentry = NULL;
175 struct group *grentry = NULL;
176
hassoba3a0bc2003-06-04 17:41:54 +0000177 if (!zprivs)
178 {
paul58a9d812003-06-11 05:12:40 +0000179 fprintf (stderr, "zprivs_init: called with NULL arg!\n");
hassoba3a0bc2003-06-04 17:41:54 +0000180 exit (1);
181 }
182
paul01245822003-05-20 01:22:17 +0000183 /* NULL privs */
184 if (! (zprivs->user || zprivs->group
185 || zprivs->cap_num_p || zprivs->cap_num_i) )
186 {
187 zprivs->change = zprivs_change_null;
188 zprivs->current_state = zprivs_state_null;
189 return;
190 }
191
192 if (zprivs->user)
193 {
194 if ( (pwentry = getpwnam (zprivs->user)) )
hassoba3a0bc2003-06-04 17:41:54 +0000195 {
196 zprivs_state.zuid = pwentry->pw_uid;
197 }
198 else
199 {
paul58a9d812003-06-11 05:12:40 +0000200 /* cant use log.h here as it depends on vty */
201 fprintf (stderr, "privs_init: could not lookup user %s\n",
202 zprivs->user);
hassoba3a0bc2003-06-04 17:41:54 +0000203 exit (1);
204 }
205 }
206
207 grentry = NULL;
208
209 if (zprivs->vty_group)
210 /* Add the vty_group to the supplementary groups so it can be chowned to */
211 {
212 if ( (grentry = getgrnam (zprivs->vty_group)) )
213 {
214 zprivs_state.vtygrp = grentry->gr_gid;
215 if ( setgroups (1, &zprivs_state.vtygrp) )
216 {
paul58a9d812003-06-11 05:12:40 +0000217 fprintf (stderr, "privs_init: could not setgroups, %s\n",
ajs6099b3b2004-11-20 02:06:59 +0000218 safe_strerror (errno) );
hassoba3a0bc2003-06-04 17:41:54 +0000219 exit (1);
220 }
221 }
paul01245822003-05-20 01:22:17 +0000222 else
223 {
paul58a9d812003-06-11 05:12:40 +0000224 fprintf (stderr, "privs_init: could not lookup vty group %s\n",
225 zprivs->vty_group);
paul01245822003-05-20 01:22:17 +0000226 exit (1);
227 }
228 }
229
230 if (zprivs->group)
231 {
hassoba3a0bc2003-06-04 17:41:54 +0000232 if ( (grentry = getgrnam (zprivs->group)) )
233 {
234 zprivs_state.zgid = grentry->gr_gid;
235 }
paul01245822003-05-20 01:22:17 +0000236 else
237 {
paul58a9d812003-06-11 05:12:40 +0000238 fprintf (stderr, "privs_init: could not lookup group %s\n",
239 zprivs->group);
paul01245822003-05-20 01:22:17 +0000240 exit (1);
241 }
paul01245822003-05-20 01:22:17 +0000242 /* change group now, forever. uid we do later */
243 if ( setregid (zprivs_state.zgid, zprivs_state.zgid) )
244 {
paul58a9d812003-06-11 05:12:40 +0000245 fprintf (stderr, "zprivs_init: could not setregid, %s\n",
ajs6099b3b2004-11-20 02:06:59 +0000246 safe_strerror (errno) );
paul01245822003-05-20 01:22:17 +0000247 exit (1);
248 }
249 }
250
251#ifdef HAVE_LCAPS
252 zprivs_state.syscaps_p = zcaps2sys (zprivs->caps_p, zprivs->cap_num_p);
253 zprivs_state.sys_num_p = zprivs->cap_num_p;
254 zprivs_state.syscaps_i = zcaps2sys (zprivs->caps_i, zprivs->cap_num_i);
255 zprivs_state.sys_num_i = zprivs->cap_num_i;
256
257 /* Tell kernel we want caps maintained across uid changes */
258 if ( prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1 )
259 {
paul58a9d812003-06-11 05:12:40 +0000260 fprintf (stderr, "privs_init: could not set PR_SET_KEEPCAPS, %s\n",
ajs6099b3b2004-11-20 02:06:59 +0000261 safe_strerror (errno) );
paul01245822003-05-20 01:22:17 +0000262 exit(1);
263 }
264
265 if ( !zprivs_state.syscaps_p )
266 {
paul58a9d812003-06-11 05:12:40 +0000267 fprintf (stderr, "privs_init: capabilities enabled, but no capabilities supplied\n");
paul01245822003-05-20 01:22:17 +0000268 }
269
270 if ( !(zprivs_state.caps = cap_init()) )
271 {
paul58a9d812003-06-11 05:12:40 +0000272 fprintf (stderr, "privs_init: failed to cap_init, %s\n",
ajs6099b3b2004-11-20 02:06:59 +0000273 safe_strerror (errno));
paul01245822003-05-20 01:22:17 +0000274 exit (1);
275 }
hassoba3a0bc2003-06-04 17:41:54 +0000276
277 /* we have caps, we have no need to ever change back the original user */
278 if (zprivs_state.zuid)
279 {
280 if ( setreuid (zprivs_state.zuid, zprivs_state.zuid) )
281 {
paul58a9d812003-06-11 05:12:40 +0000282 fprintf (stderr, "zprivs_init (cap): could not setreuid, %s\n",
ajs6099b3b2004-11-20 02:06:59 +0000283 safe_strerror (errno));
hassoba3a0bc2003-06-04 17:41:54 +0000284 exit (1);
285 }
286 }
paul01245822003-05-20 01:22:17 +0000287
288 if ( cap_clear (zprivs_state.caps) )
289 {
paul58a9d812003-06-11 05:12:40 +0000290 fprintf (stderr, "privs_init: failed to cap_clear, %s\n",
ajs6099b3b2004-11-20 02:06:59 +0000291 safe_strerror (errno));
paul01245822003-05-20 01:22:17 +0000292 exit (1);
293 }
294
295 /* set permitted caps */
296 cap_set_flag(zprivs_state.caps, CAP_PERMITTED,
297 zprivs_state.sys_num_p, zprivs_state.syscaps_p, CAP_SET);
298 cap_set_flag(zprivs_state.caps, CAP_EFFECTIVE,
299 zprivs_state.sys_num_p, zprivs_state.syscaps_p, CAP_SET);
300
paul01245822003-05-20 01:22:17 +0000301 /* set inheritable caps, if any */
302 if (zprivs_state.sys_num_i)
303 {
304 cap_set_flag(zprivs_state.caps, CAP_INHERITABLE,
305 zprivs_state.sys_num_i, zprivs_state.syscaps_i, CAP_SET);
306 }
307
hassoba3a0bc2003-06-04 17:41:54 +0000308 /* apply caps. CAP_EFFECTIVE is cleared. we'll raise the caps as
309 * and when, and only when, they are needed.
paul01245822003-05-20 01:22:17 +0000310 */
311 if ( cap_set_proc (zprivs_state.caps) )
312 {
paul58a9d812003-06-11 05:12:40 +0000313 fprintf (stderr, "privs_init: initial cap_set_proc failed\n");
paul01245822003-05-20 01:22:17 +0000314 exit (1);
315 }
316
hassoba3a0bc2003-06-04 17:41:54 +0000317 /* set methods for the caller to use */
paul01245822003-05-20 01:22:17 +0000318 zprivs->change = zprivs_change_caps;
319 zprivs->current_state = zprivs_state_caps;
320
321#elif !defined(HAVE_LCAPS)
322 /* we dont have caps. we'll need to maintain rid and saved uid
323 * and change euid back to saved uid (who we presume has all neccessary
324 * privileges) whenever we are asked to raise our privileges.
325 */
326 zprivs_state.zsuid = geteuid();
327 if ( zprivs_state.zuid )
328 {
329 if ( setreuid (-1, zprivs_state.zuid) )
330 {
paul58a9d812003-06-11 05:12:40 +0000331 fprintf (stderr, "privs_init (uid): could not setreuid, %s\n",
ajs6099b3b2004-11-20 02:06:59 +0000332 safe_strerror (errno));
paul01245822003-05-20 01:22:17 +0000333 exit (1);
334 }
335 }
336
337 zprivs->change = zprivs_change_uid;
338 zprivs->current_state = zprivs_state_uid;
339#endif /* HAVE_LCAPS */
340}
341
342void
343zprivs_terminate (void)
344{
paul33b72942003-05-20 02:22:42 +0000345
paul01245822003-05-20 01:22:17 +0000346#ifdef HAVE_LCAPS
paul33b72942003-05-20 02:22:42 +0000347
348 if (zprivs_state.caps)
349 cap_clear (zprivs_state.caps);
paul01245822003-05-20 01:22:17 +0000350
351 if ( cap_set_proc (zprivs_state.caps) )
352 {
paul33b72942003-05-20 02:22:42 +0000353 zlog_err ("privs_terminate: cap_set_proc failed, %s",
ajs6099b3b2004-11-20 02:06:59 +0000354 safe_strerror (errno) );
paul01245822003-05-20 01:22:17 +0000355 exit (1);
356 }
357
paul33b72942003-05-20 02:22:42 +0000358 if (zprivs_state.sys_num_p)
paul01245822003-05-20 01:22:17 +0000359 XFREE (MTYPE_PRIVS, zprivs_state.syscaps_p);
360
paul33b72942003-05-20 02:22:42 +0000361 if (zprivs_state.sys_num_i)
paul01245822003-05-20 01:22:17 +0000362 XFREE (MTYPE_PRIVS, zprivs_state.syscaps_i);
363
364 cap_free (zprivs_state.caps);
365#else
366 if (zprivs_state.zuid)
367 {
368 if ( setreuid (zprivs_state.zuid, zprivs_state.zuid) )
369 {
paul33b72942003-05-20 02:22:42 +0000370 zlog_err ("privs_terminate: could not setreuid, %s",
ajs6099b3b2004-11-20 02:06:59 +0000371 safe_strerror (errno) );
paul01245822003-05-20 01:22:17 +0000372 exit (1);
373 }
374 }
375#endif /* HAVE_LCAPS */
376 return;
377}
hassoba3a0bc2003-06-04 17:41:54 +0000378
379void
380zprivs_get_ids(struct zprivs_ids_t *ids)
381{
382
383 ids->uid_priv = getuid();
384 (zprivs_state.zuid) ? (ids->uid_normal = zprivs_state.zuid)
385 : (ids->uid_normal = -1);
386 (zprivs_state.zgid) ? (ids->gid_normal = zprivs_state.zgid)
387 : (ids->gid_normal = -1);
388 (zprivs_state.vtygrp) ? (ids->gid_vty = zprivs_state.vtygrp)
389 : (ids->gid_vty = -1);
390
391 return;
392}