The previous fix for 's/a/1/;s/b/2/;t one;p;:one;p' broke the case of
[oweals/busybox.git] / applets / applets.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Utility routines.
4  *
5  * Copyright (C) tons of folks.  Tracking down who wrote what
6  * isn't something I'm going to worry about...  If you wrote something
7  * here, please feel free to acknowledge your work.
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22  *
23  * Based in part on code from sash, Copyright (c) 1999 by David I. Bell
24  * Permission has been granted to redistribute this code under the GPL.
25  *
26  */
27
28 #include <unistd.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include "busybox.h"
33
34 #undef APPLET
35 #undef APPLET_NOUSAGE
36 #undef PROTOTYPES
37 #include "applets.h"
38
39 struct BB_applet *applet_using;
40
41 /* The -1 arises because of the {0,NULL,0,-1} entry above. */
42 const size_t NUM_APPLETS = (sizeof (applets) / sizeof (struct BB_applet) - 1);
43
44
45 #ifdef CONFIG_FEATURE_SUID
46
47 static void check_suid (struct BB_applet *app);
48
49 #ifdef CONFIG_FEATURE_SUID_CONFIG
50
51 #include <sys/stat.h>
52 #include <ctype.h>
53 #include "pwd_.h"
54 #include "grp_.h"
55
56 static int parse_config_file (void);
57
58 static int config_ok;
59
60 #define CONFIG_FILE "/etc/busybox.conf"
61
62 /* applets [] is const, so we have to define this "override" structure */
63 struct BB_suid_config
64 {
65   struct BB_applet *m_applet;
66
67   uid_t m_uid;
68   gid_t m_gid;
69   mode_t m_mode;
70
71   struct BB_suid_config *m_next;
72 };
73
74 static struct BB_suid_config *suid_config;
75
76 #endif /* CONFIG_FEATURE_SUID_CONFIG */
77
78 #endif /* CONFIG_FEATURE_SUID */
79
80
81
82 extern void
83 bb_show_usage (void)
84 {
85   const char *format_string;
86   const char *usage_string = usage_messages;
87   int i;
88
89   for (i = applet_using - applets; i > 0;) {
90         if (!*usage_string++) {
91           --i;
92         }
93   }
94
95   format_string = "%s\n\nUsage: %s %s\n\n";
96   if (*usage_string == '\b')
97         format_string = "%s\n\nNo help available.\n\n";
98   fprintf (stderr, format_string, bb_msg_full_version, applet_using->name,
99                    usage_string);
100
101   exit (EXIT_FAILURE);
102 }
103
104 static int
105 applet_name_compare (const void *x, const void *y)
106 {
107   const char *name = x;
108   const struct BB_applet *applet = y;
109
110   return strcmp (name, applet->name);
111 }
112
113 extern const size_t NUM_APPLETS;
114
115 struct BB_applet *
116 find_applet_by_name (const char *name)
117 {
118   return bsearch (name, applets, NUM_APPLETS, sizeof (struct BB_applet),
119                                   applet_name_compare);
120 }
121
122 void
123 run_applet_by_name (const char *name, int argc, char **argv)
124 {
125   static int recurse_level = 0;
126   extern int been_there_done_that;      /* From busybox.c */
127
128 #ifdef CONFIG_FEATURE_SUID_CONFIG
129   if (recurse_level == 0)
130         config_ok = parse_config_file ();
131 #endif
132
133   recurse_level++;
134   /* Do a binary search to find the applet entry given the name. */
135   if ((applet_using = find_applet_by_name (name)) != NULL) {
136         bb_applet_name = applet_using->name;
137         if (argv[1] && strcmp (argv[1], "--help") == 0) {
138           if (strcmp (applet_using->name, "busybox") == 0) {
139                 if (argv[2])
140                   applet_using = find_applet_by_name (argv[2]);
141                 else
142                   applet_using = NULL;
143           }
144           if (applet_using)
145                 bb_show_usage ();
146           been_there_done_that = 1;
147           busybox_main (0, NULL);
148         }
149 #ifdef CONFIG_FEATURE_SUID
150         check_suid (applet_using);
151 #endif
152
153         exit ((*(applet_using->main)) (argc, argv));
154   }
155   /* Just in case they have renamed busybox - Check argv[1] */
156   if (recurse_level == 1) {
157         run_applet_by_name ("busybox", argc, argv);
158   }
159   recurse_level--;
160 }
161
162
163 #ifdef CONFIG_FEATURE_SUID
164
165 #ifdef CONFIG_FEATURE_SUID_CONFIG
166
167 /* check if u is member of group g */
168 static int
169 ingroup (uid_t u, gid_t g)
170 {
171   struct group *grp = getgrgid (g);
172
173   if (grp) {
174         char **mem;
175
176         for (mem = grp->gr_mem; *mem; mem++) {
177           struct passwd *pwd = getpwnam (*mem);
178
179           if (pwd && (pwd->pw_uid == u))
180                 return 1;
181         }
182   }
183   return 0;
184 }
185
186 #endif
187
188
189 void
190 check_suid (struct BB_applet *applet)
191 {
192   uid_t ruid = getuid ();               /* real [ug]id */
193   uid_t rgid = getgid ();
194
195 #ifdef CONFIG_FEATURE_SUID_CONFIG
196   if (config_ok) {
197         struct BB_suid_config *sct;
198
199         for (sct = suid_config; sct; sct = sct->m_next) {
200           if (sct->m_applet == applet)
201                 break;
202         }
203         if (sct) {
204           mode_t m = sct->m_mode;
205
206           if (sct->m_uid == ruid)       /* same uid */
207                 m >>= 6;
208           else if ((sct->m_gid == rgid) || ingroup (ruid, sct->m_gid))  /* same group / in group */
209                 m >>= 3;
210
211           if (!(m & S_IXOTH))           /* is x bit not set ? */
212                 bb_error_msg_and_die ("You have no permission to run this applet!");
213
214           if ((sct->m_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {     /* *both* have to be set for sgid */
215                 if (setegid (sct->m_gid))
216                   bb_error_msg_and_die
217                         ("BusyBox binary has insufficient rights to set proper GID for applet!");
218           } else
219                 setgid (rgid);                  /* no sgid -> drop */
220
221           if (sct->m_mode & S_ISUID) {
222                 if (seteuid (sct->m_uid))
223                   bb_error_msg_and_die
224                         ("BusyBox binary has insufficient rights to set proper UID for applet!");
225           } else
226                 setuid (ruid);                  /* no suid -> drop */
227         } else {
228                 /* default: drop all priviledges */
229           setgid (rgid);
230           setuid (ruid);
231         }
232         return;
233   } else {
234 #ifndef CONFIG_FEATURE_SUID_CONFIG_QUIET
235         static int onetime = 0;
236
237         if (!onetime) {
238           onetime = 1;
239           fprintf (stderr, "Using fallback suid method\n");
240         }
241 #endif
242   }
243 #endif
244
245   if (applet->need_suid == _BB_SUID_ALWAYS) {
246         if (geteuid () != 0)
247           bb_error_msg_and_die ("This applet requires root priviledges!");
248   } else if (applet->need_suid == _BB_SUID_NEVER) {
249         setgid (rgid);                          /* drop all priviledges */
250         setuid (ruid);
251   }
252 }
253
254 #ifdef CONFIG_FEATURE_SUID_CONFIG
255
256
257 #define parse_error(x)  { err=x; goto pe_label; }
258
259
260 int
261 parse_config_file (void)
262 {
263   struct stat st;
264   char *err = 0;
265   FILE *f = 0;
266   int lc = 0;
267
268   suid_config = 0;
269
270   /* is there a config file ? */
271   if (stat (CONFIG_FILE, &st) == 0) {
272         /* is it owned by root with no write perm. for group and others ? */
273         if (S_ISREG (st.st_mode) && (st.st_uid == 0)
274                 && (!(st.st_mode & (S_IWGRP | S_IWOTH)))) {
275           /* that's ok .. then try to open it */
276           f = fopen (CONFIG_FILE, "r");
277
278           if (f) {
279                 char buffer[256];
280                 int section = 0;
281
282                 while (fgets (buffer, sizeof (buffer) - 1, f)) {
283                   char c = buffer[0];
284                   char *p;
285
286                   lc++;
287
288                   p = strchr (buffer, '#');
289                   if (p)
290                         *p = 0;
291                   p = buffer + bb_strlen (buffer);
292                   while ((p > buffer) && isspace (*--p))
293                         *p = 0;
294
295                   if (p == buffer)
296                         continue;
297
298                   if (c == '[') {
299                         p = strchr (buffer, ']');
300
301                         if (!p || (p == (buffer + 1)))  /* no matching ] or empty [] */
302                           parse_error ("malformed section header");
303
304                         *p = 0;
305
306                         if (strcasecmp (buffer + 1, "SUID") == 0)
307                           section = 1;
308                         else
309                           section = -1;         /* unknown section - just skip */
310                   } else if (section) {
311                         switch (section) {
312                         case 1:{                        /* SUID */
313                                 int l;
314                                 struct BB_applet *applet;
315
316                                 p = strchr (buffer, '=');       /* <key>[::space::]*=[::space::]*<value> */
317
318                                 if (!p || (p == (buffer + 1)))  /* no = or key is empty */
319                                   parse_error ("malformed keyword");
320
321                                 l = p - buffer;
322                                 while (isspace (buffer[--l])) {
323                                         /* skip whitespace */
324                                 }
325
326                                 buffer[l + 1] = 0;
327
328                                 if ((applet = find_applet_by_name (buffer))) {
329                                   struct BB_suid_config *sct =
330                                         xmalloc (sizeof (struct BB_suid_config));
331
332                                   sct->m_applet = applet;
333                                   sct->m_next = suid_config;
334                                   suid_config = sct;
335
336                                   while (isspace (*++p)) {
337                                         /* skip whitespace */
338                                   }
339
340                                   sct->m_mode = 0;
341
342                                   switch (*p++) {
343                                   case 'S':
344                                         sct->m_mode |= S_ISUID;
345                                         break;
346                                   case 's':
347                                         sct->m_mode |= S_ISUID;
348                                         /* no break */
349                                   case 'x':
350                                         sct->m_mode |= S_IXUSR;
351                                         break;
352                                   case '-':
353                                         break;
354                                   default:
355                                         parse_error ("invalid user mode");
356                                   }
357
358                                   switch (*p++) {
359                                   case 's':
360                                         sct->m_mode |= S_ISGID;
361                                         /* no break */
362                                   case 'x':
363                                         sct->m_mode |= S_IXGRP;
364                                         break;
365                                   case 'S':
366                                         break;
367                                   case '-':
368                                         break;
369                                   default:
370                                         parse_error ("invalid group mode");
371                                   }
372
373                                   switch (*p) {
374                                   case 't':
375                                   case 'x':
376                                         sct->m_mode |= S_IXOTH;
377                                         break;
378                                   case 'T':
379                                   case '-':
380                                         break;
381                                   default:
382                                         parse_error ("invalid other mode");
383                                   }
384
385                                   while (isspace (*++p)) {
386                                         /* skip whitespace */
387                                   }
388
389                                   if (isdigit (*p)) {
390                                         sct->m_uid = strtol (p, &p, 10);
391                                         if (*p++ != '.')
392                                           parse_error ("parsing <uid>.<gid>");
393                                   } else {
394                                         struct passwd *pwd;
395                                         char *p2 = strchr (p, '.');
396
397                                         if (!p2)
398                                           parse_error ("parsing <uid>.<gid>");
399
400                                         *p2 = 0;
401                                         pwd = getpwnam (p);
402
403                                         if (!pwd)
404                                           parse_error ("invalid user name");
405
406                                         sct->m_uid = pwd->pw_uid;
407                                         p = p2 + 1;
408                                   }
409                                   if (isdigit (*p))
410                                         sct->m_gid = strtol (p, &p, 10);
411                                   else {
412                                         struct group *grp = getgrnam (p);
413
414                                         if (!grp)
415                                           parse_error ("invalid group name");
416
417                                         sct->m_gid = grp->gr_gid;
418                                   }
419                                 }
420                                 break;
421                           }
422                         default:                        /* unknown - skip */
423                           break;
424                         }
425                   } else
426                         parse_error ("keyword not within section");
427                 }
428                 fclose (f);
429                 return 1;
430           }
431         }
432   }
433   return 0;     /* no config file or not readable (not an error) */
434
435 pe_label:
436   fprintf (stderr, "Parse error in %s, line %d: %s\n", CONFIG_FILE, lc, err);
437
438   if (f)
439         fclose (f);
440   return 0;
441 }
442
443 #endif
444
445 #endif
446
447 /* END CODE */
448 /*
449 Local Variables:
450 c-file-style: "linux"
451 c-basic-offset: 4
452 tab-width: 4
453 End:
454 */