blob: 5b7b88a548ae669cf28cafd3269f3f55789d15a8 [file] [log] [blame]
Eric Andersenaad1a882001-03-16 22:47:14 +00001/* 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 *
Glenn L McGrath2faee7b2003-05-26 14:09:12 +00009 * Based in part on code from sash, Copyright (c) 1999 by David I. Bell
Eric Andersenaad1a882001-03-16 22:47:14 +000010 * Permission has been granted to redistribute this code under the GPL.
11 *
Rob Landley73f54702006-05-01 00:53:40 +000012 * Licensed under GPLv2 or later, see file License in this tarball for details.
Eric Andersenaad1a882001-03-16 22:47:14 +000013 */
14
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +000015#include <assert.h>
Denis Vlasenkoc7ba8b92007-02-03 17:27:14 +000016#include "busybox.h"
17
Denis Vlasenko07159f02006-11-09 00:00:12 +000018/* Apparently uclibc defines __GLIBC__ (compat trick?). Oh well. */
19#if ENABLE_STATIC && defined(__GLIBC__) && !defined(__UCLIBC__)
Denis Vlasenkodf518922006-10-20 13:42:57 +000020#warning Static linking against glibc produces buggy executables
Denis Vlasenko067e3f02006-11-10 23:25:53 +000021#warning (glibc does not cope well with ld --gc-sections).
Denis Vlasenkoafea46b2006-10-29 19:37:13 +000022#warning See sources.redhat.com/bugzilla/show_bug.cgi?id=3400
Denis Vlasenko83e5d6f2006-12-18 21:49:06 +000023#warning Note that glibc is unsuitable for static linking anyway.
24#warning If you still want to do it, remove -Wl,--gc-sections
25#warning from top-level Makefile and remove this warning.
Denis Vlasenko4500c582007-05-18 07:37:06 +000026#error Aborting compilation.
Denis Vlasenkodf518922006-10-20 13:42:57 +000027#endif
28
Denis Vlasenko32b633a2007-04-09 03:05:48 +000029
30/* Declare <applet>_main() */
31#define PROTOTYPES
32#include "applets.h"
33#undef PROTOTYPES
34
Rob Landley7e21d5f2006-04-27 23:34:46 +000035#if ENABLE_SHOW_USAGE && !ENABLE_FEATURE_COMPRESS_USAGE
Denis Vlasenko32b633a2007-04-09 03:05:48 +000036/* Define usage_messages[] */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000037static const char usage_messages[] ALIGN1 = ""
Eric Andersen674b08a2004-04-06 14:28:35 +000038#define MAKE_USAGE
39#include "usage.h"
Eric Andersen674b08a2004-04-06 14:28:35 +000040#include "applets.h"
Eric Andersen674b08a2004-04-06 14:28:35 +000041;
Eric Andersen674b08a2004-04-06 14:28:35 +000042#undef MAKE_USAGE
Rob Landley73f54702006-05-01 00:53:40 +000043#else
44#define usage_messages 0
Denis Vlasenko32b633a2007-04-09 03:05:48 +000045#endif /* SHOW_USAGE */
Rob Landley7e21d5f2006-04-27 23:34:46 +000046
Denis Vlasenko335b63d2007-04-10 21:38:30 +000047/* Define struct bb_applet applets[] */
Eric Andersen2ccfef22001-03-19 19:30:24 +000048#include "applets.h"
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +000049/* The -1 arises because of the {0,NULL,0,-1} entry. */
Denis Vlasenko32b633a2007-04-09 03:05:48 +000050
Denis Vlasenko856be772007-08-17 08:29:48 +000051#if ENABLE_FEATURE_SH_STANDALONE
52const unsigned short NUM_APPLETS = ARRAY_SIZE(applets);
53#endif
Denis Vlasenko335b63d2007-04-10 21:38:30 +000054const struct bb_applet *current_applet;
Denis Vlasenko32b633a2007-04-09 03:05:48 +000055const char *applet_name ATTRIBUTE_EXTERNALLY_VISIBLE;
Denis Vlasenko473dae02007-04-11 07:04:23 +000056#if !BB_MMU
Bernhard Reutner-Fischer163516d2007-04-10 14:16:19 +000057bool re_execed;
Denis Vlasenko32b633a2007-04-09 03:05:48 +000058#endif
59
Denis Vlasenko17e3c342007-04-16 20:55:27 +000060USE_FEATURE_SUID(static uid_t ruid;) /* real uid */
Robert Grieblc9aca452002-06-04 20:06:25 +000061
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +000062#if ENABLE_FEATURE_SUID_CONFIG
Robert Grieblc9aca452002-06-04 20:06:25 +000063
Denis Vlasenko32b633a2007-04-09 03:05:48 +000064/* applets[] is const, so we have to define this "override" structure */
Denis Vlasenkoc44ab012007-04-09 03:11:58 +000065static struct BB_suid_config {
Denis Vlasenko335b63d2007-04-10 21:38:30 +000066 const struct bb_applet *m_applet;
Denis Vlasenko01a74f92006-09-23 16:34:39 +000067 uid_t m_uid;
68 gid_t m_gid;
69 mode_t m_mode;
Denis Vlasenko01a74f92006-09-23 16:34:39 +000070 struct BB_suid_config *m_next;
Bernhard Reutner-Fischer6973abc2005-10-28 09:45:07 +000071} *suid_config;
Robert Grieblc9aca452002-06-04 20:06:25 +000072
Bernhard Reutner-Fischer163516d2007-04-10 14:16:19 +000073static bool suid_cfg_readable;
Robert Grieblc9aca452002-06-04 20:06:25 +000074
Glenn L McGrathb37367a2002-08-22 13:12:40 +000075/* check if u is member of group g */
Denis Vlasenko01a74f92006-09-23 16:34:39 +000076static int ingroup(uid_t u, gid_t g)
Robert Grieblc9aca452002-06-04 20:06:25 +000077{
Denis Vlasenko01a74f92006-09-23 16:34:39 +000078 struct group *grp = getgrgid(g);
Glenn L McGrathb37367a2002-08-22 13:12:40 +000079
Denis Vlasenko01a74f92006-09-23 16:34:39 +000080 if (grp) {
81 char **mem;
Glenn L McGrathb37367a2002-08-22 13:12:40 +000082
Denis Vlasenko01a74f92006-09-23 16:34:39 +000083 for (mem = grp->gr_mem; *mem; mem++) {
84 struct passwd *pwd = getpwnam(*mem);
Glenn L McGrathb37367a2002-08-22 13:12:40 +000085
Denis Vlasenko01a74f92006-09-23 16:34:39 +000086 if (pwd && (pwd->pw_uid == u))
87 return 1;
88 }
Robert Grieblc9aca452002-06-04 20:06:25 +000089 }
Denis Vlasenko01a74f92006-09-23 16:34:39 +000090 return 0;
Robert Grieblc9aca452002-06-04 20:06:25 +000091}
92
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +000093/* This should probably be a libbb routine. In that case,
94 * I'd probably rename it to something like bb_trimmed_slice.
95 */
96static char *get_trimmed_slice(char *s, char *e)
97{
98 /* First, consider the value at e to be nul and back up until we
99 * reach a non-space char. Set the char after that (possibly at
100 * the original e) to nul. */
101 while (e-- > s) {
102 if (!isspace(*e)) {
103 break;
104 }
105 }
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000106 e[1] = '\0';
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000107
108 /* Next, advance past all leading space and return a ptr to the
109 * first non-space char; possibly the terminating nul. */
Rob Landleyea224be2006-06-18 20:20:07 +0000110 return skip_whitespace(s);
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000111}
112
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000113/* Don't depend on the tools to combine strings. */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000114static const char config_file[] ALIGN1 = "/etc/busybox.conf";
Glenn L McGrath2faee7b2003-05-26 14:09:12 +0000115
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000116/* We don't supply a value for the nul, so an index adjustment is
117 * necessary below. Also, we use unsigned short here to save some
118 * space even though these are really mode_t values. */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000119static const unsigned short mode_mask[] ALIGN2 = {
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000120 /* SST sst xxx --- */
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000121 S_ISUID, S_ISUID|S_IXUSR, S_IXUSR, 0, /* user */
122 S_ISGID, S_ISGID|S_IXGRP, S_IXGRP, 0, /* group */
123 0, S_IXOTH, S_IXOTH, 0 /* other */
124};
125
Denis Vlasenko51742f42007-04-12 00:32:05 +0000126#define parse_error(x) do { errmsg = x; goto pe_label; } while (0)
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000127
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000128static void parse_config_file(void)
Glenn L McGrath2faee7b2003-05-26 14:09:12 +0000129{
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000130 struct BB_suid_config *sct_head;
131 struct BB_suid_config *sct;
Denis Vlasenko335b63d2007-04-10 21:38:30 +0000132 const struct bb_applet *applet;
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000133 FILE *f;
Denis Vlasenkoa41fdf32007-01-29 22:51:00 +0000134 const char *errmsg;
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000135 char *s;
136 char *e;
Bernhard Reutner-Fischer163516d2007-04-10 14:16:19 +0000137 int i;
138 unsigned lc;
139 smallint section;
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000140 char buffer[256];
141 struct stat st;
Glenn L McGrath2faee7b2003-05-26 14:09:12 +0000142
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000143 assert(!suid_config); /* Should be set to NULL by bss init. */
Glenn L McGrath2faee7b2003-05-26 14:09:12 +0000144
Denis Vlasenko5f9468e2007-04-14 13:22:09 +0000145 ruid = getuid();
146 if (ruid == 0) /* run by root - don't need to even read config file */
147 return;
148
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000149 if ((stat(config_file, &st) != 0) /* No config file? */
150 || !S_ISREG(st.st_mode) /* Not a regular file? */
151 || (st.st_uid != 0) /* Not owned by root? */
152 || (st.st_mode & (S_IWGRP | S_IWOTH)) /* Writable by non-root? */
153 || !(f = fopen(config_file, "r")) /* Cannot open? */
154 ) {
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000155 return;
Robert Grieblc9aca452002-06-04 20:06:25 +0000156 }
Glenn L McGrathb37367a2002-08-22 13:12:40 +0000157
Manuel Novoa III 7b565a02004-02-17 10:16:21 +0000158 suid_cfg_readable = 1;
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000159 sct_head = NULL;
160 section = lc = 0;
Robert Grieblc9aca452002-06-04 20:06:25 +0000161
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000162 while (1) {
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000163 s = buffer;
164
165 if (!fgets(s, sizeof(buffer), f)) { /* Are we done? */
166 if (ferror(f)) { /* Make sure it wasn't a read error. */
167 parse_error("reading");
168 }
169 fclose(f);
170 suid_config = sct_head; /* Success, so set the pointer. */
171 return;
172 }
173
174 lc++; /* Got a (partial) line. */
175
176 /* If a line is too long for our buffer, we consider it an error.
177 * The following test does mistreat one corner case though.
178 * If the final line of the file does not end with a newline and
179 * yet exactly fills the buffer, it will be treated as too long
180 * even though there isn't really a problem. But it isn't really
181 * worth adding code to deal with such an unlikely situation, and
182 * we do err on the side of caution. Besides, the line would be
183 * too long if it did end with a newline. */
184 if (!strchr(s, '\n') && !feof(f)) {
185 parse_error("line too long");
186 }
187
188 /* Trim leading and trailing whitespace, ignoring comments, and
189 * check if the resulting string is empty. */
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000190 s = get_trimmed_slice(s, strchrnul(s, '#'));
191 if (!*s) {
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000192 continue;
193 }
194
195 /* Check for a section header. */
196
197 if (*s == '[') {
198 /* Unlike the old code, we ignore leading and trailing
199 * whitespace for the section name. We also require that
200 * there are no stray characters after the closing bracket. */
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000201 e = strchr(s, ']');
202 if (!e /* Missing right bracket? */
203 || e[1] /* Trailing characters? */
204 || !*(s = get_trimmed_slice(s+1, e)) /* Missing name? */
205 ) {
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000206 parse_error("section header");
207 }
208 /* Right now we only have one section so just check it.
209 * If more sections are added in the future, please don't
210 * resort to cascading ifs with multiple strcasecmp calls.
211 * That kind of bloated code is all too common. A loop
212 * and a string table would be a better choice unless the
213 * number of sections is very small. */
214 if (strcasecmp(s, "SUID") == 0) {
215 section = 1;
216 continue;
217 }
218 section = -1; /* Unknown section so set to skip. */
219 continue;
220 }
221
222 /* Process sections. */
223
224 if (section == 1) { /* SUID */
225 /* Since we trimmed leading and trailing space above, we're
226 * now looking for strings of the form
227 * <key>[::space::]*=[::space::]*<value>
228 * where both key and value could contain inner whitespace. */
229
230 /* First get the key (an applet name in our case). */
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000231 e = strchr(s, '=');
232 if (e) {
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000233 s = get_trimmed_slice(s, e);
234 }
235 if (!e || !*s) { /* Missing '=' or empty key. */
236 parse_error("keyword");
237 }
238
239 /* Ok, we have an applet name. Process the rhs if this
240 * applet is currently built in and ignore it otherwise.
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000241 * Note: this can hide config file bugs which only pop
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000242 * up when the busybox configuration is changed. */
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000243 applet = find_applet_by_name(s);
244 if (applet) {
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000245 /* Note: We currently don't check for duplicates!
246 * The last config line for each applet will be the
247 * one used since we insert at the head of the list.
248 * I suppose this could be considered a feature. */
249 sct = xmalloc(sizeof(struct BB_suid_config));
250 sct->m_applet = applet;
251 sct->m_mode = 0;
252 sct->m_next = sct_head;
253 sct_head = sct;
254
255 /* Get the specified mode. */
256
Rob Landleyea224be2006-06-18 20:20:07 +0000257 e = skip_whitespace(e+1);
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000258
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000259 for (i = 0; i < 3; i++) {
Denis Vlasenko3bc18252007-05-02 23:01:32 +0000260 /* There are 4 chars + 1 nul for each of user/group/other. */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000261 static const char mode_chars[] ALIGN1 = "Ssx-\0" "Ssx-\0" "Ttx-";
Denis Vlasenko3bc18252007-05-02 23:01:32 +0000262
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000263 const char *q;
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000264 q = strchrnul(mode_chars + 5*i, *e++);
265 if (!*q) {
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000266 parse_error("mode");
267 }
268 /* Adjust by -i to account for nul. */
269 sct->m_mode |= mode_mask[(q - mode_chars) - i];
270 }
271
272 /* Now get the the user/group info. */
Bernhard Reutner-Fischer7ca61b62006-01-15 14:04:57 +0000273
Rob Landleyea224be2006-06-18 20:20:07 +0000274 s = skip_whitespace(e);
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000275
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000276 /* Note: we require whitespace between the mode and the
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000277 * user/group info. */
278 if ((s == e) || !(e = strchr(s, '.'))) {
279 parse_error("<uid>.<gid>");
280 }
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000281 *e++ = '\0';
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000282
283 /* We can't use get_ug_id here since it would exit()
284 * if a uid or gid was not found. Oh well... */
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000285 sct->m_uid = bb_strtoul(s, NULL, 10);
286 if (errno) {
287 struct passwd *pwd = getpwnam(s);
288 if (!pwd) {
289 parse_error("user");
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000290 }
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000291 sct->m_uid = pwd->pw_uid;
292 }
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000293
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000294 sct->m_gid = bb_strtoul(e, NULL, 10);
295 if (errno) {
296 struct group *grp;
297 grp = getgrnam(e);
298 if (!grp) {
299 parse_error("group");
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000300 }
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000301 sct->m_gid = grp->gr_gid;
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000302 }
303 }
304 continue;
305 }
306
307 /* Unknown sections are ignored. */
308
309 /* Encountering configuration lines prior to seeing a
310 * section header is treated as an error. This is how
Eric Andersenaff114c2004-04-14 17:51:38 +0000311 * the old code worked, but it may not be desirable.
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000312 * We may want to simply ignore such lines in case they
313 * are used in some future version of busybox. */
314 if (!section) {
315 parse_error("keyword outside section");
316 }
317
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000318 } /* while (1) */
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000319
320 pe_label:
321 fprintf(stderr, "Parse error in %s, line %d: %s\n",
Denis Vlasenkoa41fdf32007-01-29 22:51:00 +0000322 config_file, lc, errmsg);
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000323
324 fclose(f);
325 /* Release any allocated memory before returning. */
326 while (sct_head) {
327 sct = sct_head->m_next;
328 free(sct_head);
329 sct_head = sct;
330 }
Robert Grieblc9aca452002-06-04 20:06:25 +0000331}
Rob Landley8a7a6782005-09-05 04:13:33 +0000332#else
Denis Vlasenko5f9468e2007-04-14 13:22:09 +0000333static inline void parse_config_file(void)
334{
Denis Vlasenko17e3c342007-04-16 20:55:27 +0000335 USE_FEATURE_SUID(ruid = getuid();)
Denis Vlasenko5f9468e2007-04-14 13:22:09 +0000336}
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000337#endif /* FEATURE_SUID_CONFIG */
338
Rob Landley8a7a6782005-09-05 04:13:33 +0000339
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000340#if ENABLE_FEATURE_SUID
Denis Vlasenko335b63d2007-04-10 21:38:30 +0000341static void check_suid(const struct bb_applet *applet)
Rob Landley8a7a6782005-09-05 04:13:33 +0000342{
Denis Vlasenko3bc18252007-05-02 23:01:32 +0000343 gid_t rgid; /* real gid */
Denis Vlasenko5f9468e2007-04-14 13:22:09 +0000344
345 if (ruid == 0) /* set by parse_config_file() */
346 return; /* run by root - no need to check more */
347 rgid = getgid();
Rob Landley8a7a6782005-09-05 04:13:33 +0000348
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000349#if ENABLE_FEATURE_SUID_CONFIG
Denis Vlasenko01a74f92006-09-23 16:34:39 +0000350 if (suid_cfg_readable) {
Denis Vlasenko3349fc42007-05-04 14:54:36 +0000351 uid_t uid;
Denis Vlasenko01a74f92006-09-23 16:34:39 +0000352 struct BB_suid_config *sct;
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000353 mode_t m;
Rob Landley8a7a6782005-09-05 04:13:33 +0000354
Denis Vlasenko01a74f92006-09-23 16:34:39 +0000355 for (sct = suid_config; sct; sct = sct->m_next) {
356 if (sct->m_applet == applet)
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000357 goto found;
Denis Vlasenko01a74f92006-09-23 16:34:39 +0000358 }
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000359 /* default: drop all privileges */
360 xsetgid(rgid);
361 xsetuid(ruid);
362 return;
363 found:
364 m = sct->m_mode;
365 if (sct->m_uid == ruid)
366 /* same uid */
367 m >>= 6;
368 else if ((sct->m_gid == rgid) || ingroup(ruid, sct->m_gid))
369 /* same group / in group */
370 m >>= 3;
Rob Landley8a7a6782005-09-05 04:13:33 +0000371
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000372 if (!(m & S_IXOTH)) /* is x bit not set ? */
373 bb_error_msg_and_die("you have no permission to run this applet!");
Rob Landley8a7a6782005-09-05 04:13:33 +0000374
Denis Vlasenko3bc18252007-05-02 23:01:32 +0000375 /* _both_ sgid and group_exec have to be set for setegid */
376 if ((sct->m_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP))
377 rgid = sct->m_gid;
378 /* else (no setegid) we will set egid = rgid */
379
380 /* We set effective AND saved ids. If saved-id is not set
381 * like we do below, seteiud(0) can still later succeed! */
382 if (setresgid(-1, rgid, rgid))
383 bb_perror_msg_and_die("setresgid");
384
385 /* do we have to set effective uid? */
386 uid = ruid;
387 if (sct->m_mode & S_ISUID)
388 uid = sct->m_uid;
389 /* else (no seteuid) we will set euid = ruid */
390
391 if (setresuid(-1, uid, uid))
392 bb_perror_msg_and_die("setresuid");
Denis Vlasenko01a74f92006-09-23 16:34:39 +0000393 return;
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000394 }
395#if !ENABLE_FEATURE_SUID_CONFIG_QUIET
396 {
Bernhard Reutner-Fischer163516d2007-04-10 14:16:19 +0000397 static bool onetime = 0;
Rob Landley8a7a6782005-09-05 04:13:33 +0000398
Denis Vlasenko01a74f92006-09-23 16:34:39 +0000399 if (!onetime) {
400 onetime = 1;
Denis Vlasenko13c5a682006-10-16 22:39:51 +0000401 fprintf(stderr, "Using fallback suid method\n");
Denis Vlasenko01a74f92006-09-23 16:34:39 +0000402 }
Rob Landley8a7a6782005-09-05 04:13:33 +0000403 }
404#endif
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000405#endif
Robert Grieblc9aca452002-06-04 20:06:25 +0000406
Denis Vlasenko01a74f92006-09-23 16:34:39 +0000407 if (applet->need_suid == _BB_SUID_ALWAYS) {
Denis Vlasenko3bc18252007-05-02 23:01:32 +0000408 /* Real uid is not 0. If euid isn't 0 too, suid bit
409 * is most probably not set on our executable */
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000410 if (geteuid())
411 bb_error_msg_and_die("applet requires root privileges!");
Denis Vlasenko01a74f92006-09-23 16:34:39 +0000412 } else if (applet->need_suid == _BB_SUID_NEVER) {
Denis Vlasenko5f9468e2007-04-14 13:22:09 +0000413 xsetgid(rgid); /* drop all privileges */
Denis Vlasenko01a74f92006-09-23 16:34:39 +0000414 xsetuid(ruid);
415 }
Rob Landley8a7a6782005-09-05 04:13:33 +0000416}
417#else
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000418#define check_suid(x) ((void)0)
419#endif /* FEATURE_SUID */
Rob Landley8a7a6782005-09-05 04:13:33 +0000420
421
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000422#if ENABLE_FEATURE_COMPRESS_USAGE
Rob Landley8a7a6782005-09-05 04:13:33 +0000423
Rob Landley7e21d5f2006-04-27 23:34:46 +0000424#include "usage_compressed.h"
425#include "unarchive.h"
426
427static const char *unpack_usage_messages(void)
428{
Denis Vlasenkoc6758a02007-04-10 21:40:19 +0000429 char *outbuf = NULL;
430 bunzip_data *bd;
431 int i;
Rob Landley7e21d5f2006-04-27 23:34:46 +0000432
Denis Vlasenkoc6758a02007-04-10 21:40:19 +0000433 i = start_bunzip(&bd,
434 /* src_fd: */ -1,
435 /* inbuf: */ packed_usage,
436 /* len: */ sizeof(packed_usage));
437 /* read_bunzip can longjmp to start_bunzip, and ultimately
438 * end up here with i != 0 on read data errors! Not trivial */
439 if (!i) {
440 /* Cannot use xmalloc: will leak bd in NOFORK case! */
441 outbuf = malloc_or_warn(SIZEOF_usage_messages);
442 if (outbuf)
443 read_bunzip(bd, outbuf, SIZEOF_usage_messages);
Rob Landley7e21d5f2006-04-27 23:34:46 +0000444 }
Denis Vlasenkoc6758a02007-04-10 21:40:19 +0000445 dealloc_bunzip(bd);
446 return outbuf;
Rob Landley7e21d5f2006-04-27 23:34:46 +0000447}
Denis Vlasenkoc6758a02007-04-10 21:40:19 +0000448#define dealloc_usage_messages(s) free(s)
449
Rob Landley7e21d5f2006-04-27 23:34:46 +0000450#else
Denis Vlasenkoc6758a02007-04-10 21:40:19 +0000451
Rob Landley1801e9c2006-05-03 20:19:14 +0000452#define unpack_usage_messages() usage_messages
Denis Vlasenkoc6758a02007-04-10 21:40:19 +0000453#define dealloc_usage_messages(s) ((void)(s))
454
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000455#endif /* FEATURE_COMPRESS_USAGE */
456
Rob Landley8a7a6782005-09-05 04:13:33 +0000457
Denis Vlasenko01a74f92006-09-23 16:34:39 +0000458void bb_show_usage(void)
Rob Landley8a7a6782005-09-05 04:13:33 +0000459{
Rob Landley7e21d5f2006-04-27 23:34:46 +0000460 if (ENABLE_SHOW_USAGE) {
461 const char *format_string;
Denis Vlasenkoc6758a02007-04-10 21:40:19 +0000462 const char *p;
463 const char *usage_string = p = unpack_usage_messages();
Rob Landley7e21d5f2006-04-27 23:34:46 +0000464 int i;
Rob Landley8a7a6782005-09-05 04:13:33 +0000465
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000466 i = current_applet - applets;
467 while (i) {
Denis Vlasenkoc6758a02007-04-10 21:40:19 +0000468 while (*p++) continue;
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000469 i--;
470 }
Rob Landley7e21d5f2006-04-27 23:34:46 +0000471
Denis Vlasenkoca525b42007-06-13 12:27:17 +0000472 fprintf(stderr, "%s multi-call binary\n", bb_banner);
473 format_string = "\nUsage: %s %s\n\n";
Denis Vlasenkoc6758a02007-04-10 21:40:19 +0000474 if (*p == '\b')
Denis Vlasenkoca525b42007-06-13 12:27:17 +0000475 format_string = "\nNo help available.\n\n";
476 fprintf(stderr, format_string, applet_name, p);
Denis Vlasenkoc6758a02007-04-10 21:40:19 +0000477 dealloc_usage_messages((char*)usage_string);
Rob Landley8a7a6782005-09-05 04:13:33 +0000478 }
Denis Vlasenko335b63d2007-04-10 21:38:30 +0000479 xfunc_die();
Rob Landley8a7a6782005-09-05 04:13:33 +0000480}
481
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000482
Rob Landley53437472006-07-16 08:14:35 +0000483static int applet_name_compare(const void *name, const void *vapplet)
Rob Landley8a7a6782005-09-05 04:13:33 +0000484{
Denis Vlasenko335b63d2007-04-10 21:38:30 +0000485 const struct bb_applet *applet = vapplet;
Rob Landley8a7a6782005-09-05 04:13:33 +0000486
Denis Vlasenko01a74f92006-09-23 16:34:39 +0000487 return strcmp(name, applet->name);
Rob Landley8a7a6782005-09-05 04:13:33 +0000488}
489
Denis Vlasenko335b63d2007-04-10 21:38:30 +0000490const struct bb_applet *find_applet_by_name(const char *name)
Rob Landley8a7a6782005-09-05 04:13:33 +0000491{
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000492 /* Do a binary search to find the applet entry given the name. */
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000493 return bsearch(name, applets, ARRAY_SIZE(applets)-1, sizeof(applets[0]),
Denis Vlasenko01a74f92006-09-23 16:34:39 +0000494 applet_name_compare);
Rob Landley8a7a6782005-09-05 04:13:33 +0000495}
496
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000497
498#if ENABLE_FEATURE_INSTALLER
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000499/* create (sym)links for each applet */
500static void install_links(const char *busybox, int use_symbolic_links)
501{
Denis Vlasenko335b63d2007-04-10 21:38:30 +0000502 /* directory table
503 * this should be consistent w/ the enum,
504 * busybox.h::bb_install_loc_t, or else... */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000505 static const char usr_bin [] ALIGN1 = "/usr/bin";
506 static const char usr_sbin[] ALIGN1 = "/usr/sbin";
Denis Vlasenko335b63d2007-04-10 21:38:30 +0000507 static const char *const install_dir[] = {
508 &usr_bin [8], /* "", equivalent to "/" for concat_path_file() */
509 &usr_bin [4], /* "/bin" */
510 &usr_sbin[4], /* "/sbin" */
511 usr_bin,
512 usr_sbin
513 };
514
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000515 int (*lf)(const char *, const char *) = link;
516 char *fpc;
517 int i;
518 int rc;
519
520 if (use_symbolic_links)
521 lf = symlink;
522
523 for (i = 0; applets[i].name != NULL; i++) {
524 fpc = concat_path_file(
Denis Vlasenko335b63d2007-04-10 21:38:30 +0000525 install_dir[applets[i].install_loc],
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000526 applets[i].name);
527 rc = lf(busybox, fpc);
528 if (rc != 0 && errno != EEXIST) {
529 bb_perror_msg("%s", fpc);
530 }
531 free(fpc);
532 }
533}
534#else
535#define install_links(x,y) ((void)0)
536#endif /* FEATURE_INSTALLER */
537
538
539/* If we were called as "busybox..." */
Denis Vlasenkof5294e12007-04-14 10:09:57 +0000540static int busybox_main(char **argv)
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000541{
Denis Vlasenko2ace1e32007-04-18 21:00:21 +0000542 if (!argv[1]) {
543 /* Called without arguments */
544 const struct bb_applet *a;
545 int col, output_width;
546 help:
547 output_width = 80;
548 if (ENABLE_FEATURE_AUTOWIDTH) {
Denis Vlasenkobeffd432007-09-05 11:30:34 +0000549 /* Obtain the terminal width */
Denis Vlasenko2ace1e32007-04-18 21:00:21 +0000550 get_terminal_width_height(0, &output_width, NULL);
551 }
552 /* leading tab and room to wrap */
553 output_width -= sizeof("start-stop-daemon, ") + 8;
554
Denis Vlasenkoca525b42007-06-13 12:27:17 +0000555 printf("%s multi-call binary\n", bb_banner); /* reuse const string... */
Denis Vlasenko9b0a4392007-09-20 19:44:10 +0000556 printf("Copyright (C) 1998-2006 Erik Andersen, Rob Landley, and others.\n"
557 "Licensed under GPLv2. See source distribution for full notice.\n"
Denis Vlasenko2ace1e32007-04-18 21:00:21 +0000558 "\n"
559 "Usage: busybox [function] [arguments]...\n"
560 " or: [function] [arguments]...\n"
561 "\n"
562 "\tBusyBox is a multi-call binary that combines many common Unix\n"
563 "\tutilities into a single executable. Most people will create a\n"
564 "\tlink to busybox for each function they wish to use and BusyBox\n"
565 "\twill act like whatever it was invoked as!\n"
Denis Vlasenkoca525b42007-06-13 12:27:17 +0000566 "\nCurrently defined functions:\n");
Denis Vlasenko2ace1e32007-04-18 21:00:21 +0000567 col = 0;
568 a = applets;
569 while (a->name) {
570 if (col > output_width) {
571 puts(",");
572 col = 0;
573 }
574 col += printf("%s%s", (col ? ", " : "\t"), a->name);
575 a++;
576 }
577 puts("\n");
578 return 0;
579 }
580
581 if (ENABLE_FEATURE_INSTALLER && strcmp(argv[1], "--install") == 0) {
Denis Vlasenkobdbbb7e2007-06-08 15:02:55 +0000582 const char *busybox;
Denis Vlasenkobeffd432007-09-05 11:30:34 +0000583 busybox = xmalloc_readlink(bb_busybox_exec_path);
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000584 if (!busybox)
Denis Vlasenkobdbbb7e2007-06-08 15:02:55 +0000585 busybox = bb_busybox_exec_path;
586 /* -s makes symlinks */
Denis Vlasenkobeffd432007-09-05 11:30:34 +0000587 install_links(busybox, argv[2] && strcmp(argv[2], "-s") == 0);
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000588 return 0;
589 }
590
Denis Vlasenko2ace1e32007-04-18 21:00:21 +0000591 if (strcmp(argv[1], "--help") == 0) {
592 /* "busybox --help [<applet>]" */
593 if (!argv[2])
594 goto help;
595 /* convert to "<applet> --help" */
596 argv[0] = argv[2];
597 argv[2] = NULL;
Denis Vlasenko2dfdd442007-04-09 03:29:43 +0000598 } else {
Denis Vlasenko2ace1e32007-04-18 21:00:21 +0000599 /* "busybox <applet> arg1 arg2 ..." */
600 argv++;
Denis Vlasenko2dfdd442007-04-09 03:29:43 +0000601 }
Denis Vlasenko737d1312007-08-25 18:25:24 +0000602 /* We support "busybox /a/path/to/applet args..." too. Allows for
603 * "#!/bin/busybox"-style wrappers */
Denis Vlasenko818322b2007-09-24 18:27:04 +0000604 applet_name = bb_get_last_path_component_nostrip(argv[0]);
Denis Vlasenko737d1312007-08-25 18:25:24 +0000605 run_applet_and_exit(applet_name, argv);
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000606 bb_error_msg_and_die("applet not found");
607}
608
Denis Vlasenkof5294e12007-04-14 10:09:57 +0000609void run_current_applet_and_exit(char **argv)
Denis Vlasenkoc44ab012007-04-09 03:11:58 +0000610{
Denis Vlasenkof5294e12007-04-14 10:09:57 +0000611 int argc = 1;
612
613 while (argv[argc])
614 argc++;
615
Denis Vlasenko831a20f2007-04-12 12:27:32 +0000616 /* Reinit some shared global data */
617 optind = 1;
618 xfunc_error_retval = EXIT_FAILURE;
619
Denis Vlasenkoc44ab012007-04-09 03:11:58 +0000620 applet_name = current_applet->name;
621 if (argc == 2 && !strcmp(argv[1], "--help"))
622 bb_show_usage();
623 if (ENABLE_FEATURE_SUID)
624 check_suid(current_applet);
625 exit(current_applet->main(argc, argv));
626}
627
Denis Vlasenkof5294e12007-04-14 10:09:57 +0000628void run_applet_and_exit(const char *name, char **argv)
Rob Landley8a7a6782005-09-05 04:13:33 +0000629{
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000630 current_applet = find_applet_by_name(name);
Denis Vlasenkoc44ab012007-04-09 03:11:58 +0000631 if (current_applet)
Denis Vlasenkof5294e12007-04-14 10:09:57 +0000632 run_current_applet_and_exit(argv);
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000633 if (!strncmp(name, "busybox", 7))
Denis Vlasenkof5294e12007-04-14 10:09:57 +0000634 exit(busybox_main(argv));
Rob Landley8a7a6782005-09-05 04:13:33 +0000635}
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000636
637
Denis Vlasenkofad2b862007-05-31 22:16:38 +0000638#ifdef __GLIBC__
639/* Make it reside in R/W memory: */
640int *const bb_errno __attribute__ ((section (".data")));
641#endif
642
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000643int main(int argc, char **argv)
644{
Denis Vlasenkofad2b862007-05-31 22:16:38 +0000645#ifdef __GLIBC__
646 (*(int **)&bb_errno) = __errno_location();
647#endif
648
Denis Vlasenko473dae02007-04-11 07:04:23 +0000649#if !BB_MMU
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000650 /* NOMMU re-exec trick sets high-order bit in first byte of name */
Denis Vlasenkof1a71412007-04-10 23:32:37 +0000651 if (argv[0][0] & 0x80) {
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000652 re_execed = 1;
Denis Vlasenkof1a71412007-04-10 23:32:37 +0000653 argv[0][0] &= 0x7f;
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000654 }
655#endif
Denis Vlasenkof1a71412007-04-10 23:32:37 +0000656 applet_name = argv[0];
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000657 if (applet_name[0] == '-')
658 applet_name++;
Denis Vlasenkodc757aa2007-06-30 08:04:05 +0000659 applet_name = bb_basename(applet_name);
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000660
Denis Vlasenko5f9468e2007-04-14 13:22:09 +0000661 parse_config_file(); /* ...maybe, if FEATURE_SUID_CONFIG */
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000662
663 /* Set locale for everybody except 'init' */
664 if (ENABLE_LOCALE_SUPPORT && getpid() != 1)
665 setlocale(LC_ALL, "");
666
Denis Vlasenkof5294e12007-04-14 10:09:57 +0000667 run_applet_and_exit(applet_name, argv);
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000668 bb_error_msg_and_die("applet not found");
669}