blob: cff792fb718206bae4d0bae50dc791c175147b89 [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 Vlasenko831a20f2007-04-12 12:27:32 +000037static const char usage_messages[] = ""
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 Vlasenko335b63d2007-04-10 21:38:30 +000050const unsigned short NUM_APPLETS = sizeof(applets) / sizeof(applets[0]) - 1;
Denis Vlasenko32b633a2007-04-09 03:05:48 +000051
Denis Vlasenko335b63d2007-04-10 21:38:30 +000052const struct bb_applet *current_applet;
Denis Vlasenko32b633a2007-04-09 03:05:48 +000053const char *applet_name ATTRIBUTE_EXTERNALLY_VISIBLE;
Denis Vlasenko473dae02007-04-11 07:04:23 +000054#if !BB_MMU
Bernhard Reutner-Fischer163516d2007-04-10 14:16:19 +000055bool re_execed;
Denis Vlasenko32b633a2007-04-09 03:05:48 +000056#endif
57
Denis Vlasenko17e3c342007-04-16 20:55:27 +000058USE_FEATURE_SUID(static uid_t ruid;) /* real uid */
Robert Grieblc9aca452002-06-04 20:06:25 +000059
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +000060#if ENABLE_FEATURE_SUID_CONFIG
Robert Grieblc9aca452002-06-04 20:06:25 +000061
Denis Vlasenko32b633a2007-04-09 03:05:48 +000062/* applets[] is const, so we have to define this "override" structure */
Denis Vlasenkoc44ab012007-04-09 03:11:58 +000063static struct BB_suid_config {
Denis Vlasenko335b63d2007-04-10 21:38:30 +000064 const struct bb_applet *m_applet;
Denis Vlasenko01a74f92006-09-23 16:34:39 +000065 uid_t m_uid;
66 gid_t m_gid;
67 mode_t m_mode;
Denis Vlasenko01a74f92006-09-23 16:34:39 +000068 struct BB_suid_config *m_next;
Bernhard Reutner-Fischer6973abc2005-10-28 09:45:07 +000069} *suid_config;
Robert Grieblc9aca452002-06-04 20:06:25 +000070
Bernhard Reutner-Fischer163516d2007-04-10 14:16:19 +000071static bool suid_cfg_readable;
Robert Grieblc9aca452002-06-04 20:06:25 +000072
Glenn L McGrathb37367a2002-08-22 13:12:40 +000073/* check if u is member of group g */
Denis Vlasenko01a74f92006-09-23 16:34:39 +000074static int ingroup(uid_t u, gid_t g)
Robert Grieblc9aca452002-06-04 20:06:25 +000075{
Denis Vlasenko01a74f92006-09-23 16:34:39 +000076 struct group *grp = getgrgid(g);
Glenn L McGrathb37367a2002-08-22 13:12:40 +000077
Denis Vlasenko01a74f92006-09-23 16:34:39 +000078 if (grp) {
79 char **mem;
Glenn L McGrathb37367a2002-08-22 13:12:40 +000080
Denis Vlasenko01a74f92006-09-23 16:34:39 +000081 for (mem = grp->gr_mem; *mem; mem++) {
82 struct passwd *pwd = getpwnam(*mem);
Glenn L McGrathb37367a2002-08-22 13:12:40 +000083
Denis Vlasenko01a74f92006-09-23 16:34:39 +000084 if (pwd && (pwd->pw_uid == u))
85 return 1;
86 }
Robert Grieblc9aca452002-06-04 20:06:25 +000087 }
Denis Vlasenko01a74f92006-09-23 16:34:39 +000088 return 0;
Robert Grieblc9aca452002-06-04 20:06:25 +000089}
90
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +000091/* This should probably be a libbb routine. In that case,
92 * I'd probably rename it to something like bb_trimmed_slice.
93 */
94static char *get_trimmed_slice(char *s, char *e)
95{
96 /* First, consider the value at e to be nul and back up until we
97 * reach a non-space char. Set the char after that (possibly at
98 * the original e) to nul. */
99 while (e-- > s) {
100 if (!isspace(*e)) {
101 break;
102 }
103 }
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000104 e[1] = '\0';
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000105
106 /* Next, advance past all leading space and return a ptr to the
107 * first non-space char; possibly the terminating nul. */
Rob Landleyea224be2006-06-18 20:20:07 +0000108 return skip_whitespace(s);
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000109}
110
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000111/* Don't depend on the tools to combine strings. */
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000112static const char config_file[] = "/etc/busybox.conf";
Glenn L McGrath2faee7b2003-05-26 14:09:12 +0000113
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000114/* We don't supply a value for the nul, so an index adjustment is
115 * necessary below. Also, we use unsigned short here to save some
116 * space even though these are really mode_t values. */
117static const unsigned short mode_mask[] = {
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000118 /* SST sst xxx --- */
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000119 S_ISUID, S_ISUID|S_IXUSR, S_IXUSR, 0, /* user */
120 S_ISGID, S_ISGID|S_IXGRP, S_IXGRP, 0, /* group */
121 0, S_IXOTH, S_IXOTH, 0 /* other */
122};
123
Denis Vlasenko51742f42007-04-12 00:32:05 +0000124#define parse_error(x) do { errmsg = x; goto pe_label; } while (0)
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000125
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000126static void parse_config_file(void)
Glenn L McGrath2faee7b2003-05-26 14:09:12 +0000127{
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000128 struct BB_suid_config *sct_head;
129 struct BB_suid_config *sct;
Denis Vlasenko335b63d2007-04-10 21:38:30 +0000130 const struct bb_applet *applet;
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000131 FILE *f;
Denis Vlasenkoa41fdf32007-01-29 22:51:00 +0000132 const char *errmsg;
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000133 char *s;
134 char *e;
Bernhard Reutner-Fischer163516d2007-04-10 14:16:19 +0000135 int i;
136 unsigned lc;
137 smallint section;
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000138 char buffer[256];
139 struct stat st;
Glenn L McGrath2faee7b2003-05-26 14:09:12 +0000140
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000141 assert(!suid_config); /* Should be set to NULL by bss init. */
Glenn L McGrath2faee7b2003-05-26 14:09:12 +0000142
Denis Vlasenko5f9468e2007-04-14 13:22:09 +0000143 ruid = getuid();
144 if (ruid == 0) /* run by root - don't need to even read config file */
145 return;
146
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000147 if ((stat(config_file, &st) != 0) /* No config file? */
148 || !S_ISREG(st.st_mode) /* Not a regular file? */
149 || (st.st_uid != 0) /* Not owned by root? */
150 || (st.st_mode & (S_IWGRP | S_IWOTH)) /* Writable by non-root? */
151 || !(f = fopen(config_file, "r")) /* Cannot open? */
152 ) {
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000153 return;
Robert Grieblc9aca452002-06-04 20:06:25 +0000154 }
Glenn L McGrathb37367a2002-08-22 13:12:40 +0000155
Manuel Novoa III 7b565a02004-02-17 10:16:21 +0000156 suid_cfg_readable = 1;
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000157 sct_head = NULL;
158 section = lc = 0;
Robert Grieblc9aca452002-06-04 20:06:25 +0000159
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000160 while (1) {
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000161 s = buffer;
162
163 if (!fgets(s, sizeof(buffer), f)) { /* Are we done? */
164 if (ferror(f)) { /* Make sure it wasn't a read error. */
165 parse_error("reading");
166 }
167 fclose(f);
168 suid_config = sct_head; /* Success, so set the pointer. */
169 return;
170 }
171
172 lc++; /* Got a (partial) line. */
173
174 /* If a line is too long for our buffer, we consider it an error.
175 * The following test does mistreat one corner case though.
176 * If the final line of the file does not end with a newline and
177 * yet exactly fills the buffer, it will be treated as too long
178 * even though there isn't really a problem. But it isn't really
179 * worth adding code to deal with such an unlikely situation, and
180 * we do err on the side of caution. Besides, the line would be
181 * too long if it did end with a newline. */
182 if (!strchr(s, '\n') && !feof(f)) {
183 parse_error("line too long");
184 }
185
186 /* Trim leading and trailing whitespace, ignoring comments, and
187 * check if the resulting string is empty. */
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000188 s = get_trimmed_slice(s, strchrnul(s, '#'));
189 if (!*s) {
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000190 continue;
191 }
192
193 /* Check for a section header. */
194
195 if (*s == '[') {
196 /* Unlike the old code, we ignore leading and trailing
197 * whitespace for the section name. We also require that
198 * there are no stray characters after the closing bracket. */
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000199 e = strchr(s, ']');
200 if (!e /* Missing right bracket? */
201 || e[1] /* Trailing characters? */
202 || !*(s = get_trimmed_slice(s+1, e)) /* Missing name? */
203 ) {
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000204 parse_error("section header");
205 }
206 /* Right now we only have one section so just check it.
207 * If more sections are added in the future, please don't
208 * resort to cascading ifs with multiple strcasecmp calls.
209 * That kind of bloated code is all too common. A loop
210 * and a string table would be a better choice unless the
211 * number of sections is very small. */
212 if (strcasecmp(s, "SUID") == 0) {
213 section = 1;
214 continue;
215 }
216 section = -1; /* Unknown section so set to skip. */
217 continue;
218 }
219
220 /* Process sections. */
221
222 if (section == 1) { /* SUID */
223 /* Since we trimmed leading and trailing space above, we're
224 * now looking for strings of the form
225 * <key>[::space::]*=[::space::]*<value>
226 * where both key and value could contain inner whitespace. */
227
228 /* First get the key (an applet name in our case). */
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000229 e = strchr(s, '=');
230 if (e) {
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000231 s = get_trimmed_slice(s, e);
232 }
233 if (!e || !*s) { /* Missing '=' or empty key. */
234 parse_error("keyword");
235 }
236
237 /* Ok, we have an applet name. Process the rhs if this
238 * applet is currently built in and ignore it otherwise.
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000239 * Note: this can hide config file bugs which only pop
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000240 * up when the busybox configuration is changed. */
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000241 applet = find_applet_by_name(s);
242 if (applet) {
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000243 /* Note: We currently don't check for duplicates!
244 * The last config line for each applet will be the
245 * one used since we insert at the head of the list.
246 * I suppose this could be considered a feature. */
247 sct = xmalloc(sizeof(struct BB_suid_config));
248 sct->m_applet = applet;
249 sct->m_mode = 0;
250 sct->m_next = sct_head;
251 sct_head = sct;
252
253 /* Get the specified mode. */
254
Rob Landleyea224be2006-06-18 20:20:07 +0000255 e = skip_whitespace(e+1);
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000256
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000257 for (i = 0; i < 3; i++) {
Denis Vlasenko3bc18252007-05-02 23:01:32 +0000258 /* There are 4 chars + 1 nul for each of user/group/other. */
259 static const char mode_chars[] = "Ssx-\0" "Ssx-\0" "Ttx-";
260
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000261 const char *q;
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000262 q = strchrnul(mode_chars + 5*i, *e++);
263 if (!*q) {
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000264 parse_error("mode");
265 }
266 /* Adjust by -i to account for nul. */
267 sct->m_mode |= mode_mask[(q - mode_chars) - i];
268 }
269
270 /* Now get the the user/group info. */
Bernhard Reutner-Fischer7ca61b62006-01-15 14:04:57 +0000271
Rob Landleyea224be2006-06-18 20:20:07 +0000272 s = skip_whitespace(e);
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000273
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000274 /* Note: we require whitespace between the mode and the
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000275 * user/group info. */
276 if ((s == e) || !(e = strchr(s, '.'))) {
277 parse_error("<uid>.<gid>");
278 }
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000279 *e++ = '\0';
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000280
281 /* We can't use get_ug_id here since it would exit()
282 * if a uid or gid was not found. Oh well... */
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000283 sct->m_uid = bb_strtoul(s, NULL, 10);
284 if (errno) {
285 struct passwd *pwd = getpwnam(s);
286 if (!pwd) {
287 parse_error("user");
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000288 }
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000289 sct->m_uid = pwd->pw_uid;
290 }
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000291
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000292 sct->m_gid = bb_strtoul(e, NULL, 10);
293 if (errno) {
294 struct group *grp;
295 grp = getgrnam(e);
296 if (!grp) {
297 parse_error("group");
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000298 }
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000299 sct->m_gid = grp->gr_gid;
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000300 }
301 }
302 continue;
303 }
304
305 /* Unknown sections are ignored. */
306
307 /* Encountering configuration lines prior to seeing a
308 * section header is treated as an error. This is how
Eric Andersenaff114c2004-04-14 17:51:38 +0000309 * the old code worked, but it may not be desirable.
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000310 * We may want to simply ignore such lines in case they
311 * are used in some future version of busybox. */
312 if (!section) {
313 parse_error("keyword outside section");
314 }
315
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000316 } /* while (1) */
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000317
318 pe_label:
319 fprintf(stderr, "Parse error in %s, line %d: %s\n",
Denis Vlasenkoa41fdf32007-01-29 22:51:00 +0000320 config_file, lc, errmsg);
Manuel Novoa III 31b98dd2004-02-01 10:03:05 +0000321
322 fclose(f);
323 /* Release any allocated memory before returning. */
324 while (sct_head) {
325 sct = sct_head->m_next;
326 free(sct_head);
327 sct_head = sct;
328 }
Robert Grieblc9aca452002-06-04 20:06:25 +0000329}
Rob Landley8a7a6782005-09-05 04:13:33 +0000330#else
Denis Vlasenko5f9468e2007-04-14 13:22:09 +0000331static inline void parse_config_file(void)
332{
Denis Vlasenko17e3c342007-04-16 20:55:27 +0000333 USE_FEATURE_SUID(ruid = getuid();)
Denis Vlasenko5f9468e2007-04-14 13:22:09 +0000334}
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000335#endif /* FEATURE_SUID_CONFIG */
336
Rob Landley8a7a6782005-09-05 04:13:33 +0000337
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000338#if ENABLE_FEATURE_SUID
Denis Vlasenko335b63d2007-04-10 21:38:30 +0000339static void check_suid(const struct bb_applet *applet)
Rob Landley8a7a6782005-09-05 04:13:33 +0000340{
Denis Vlasenko3bc18252007-05-02 23:01:32 +0000341 gid_t rgid; /* real gid */
Denis Vlasenko5f9468e2007-04-14 13:22:09 +0000342
343 if (ruid == 0) /* set by parse_config_file() */
344 return; /* run by root - no need to check more */
345 rgid = getgid();
Rob Landley8a7a6782005-09-05 04:13:33 +0000346
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000347#if ENABLE_FEATURE_SUID_CONFIG
Denis Vlasenko01a74f92006-09-23 16:34:39 +0000348 if (suid_cfg_readable) {
Denis Vlasenko3349fc42007-05-04 14:54:36 +0000349 uid_t uid;
Denis Vlasenko01a74f92006-09-23 16:34:39 +0000350 struct BB_suid_config *sct;
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000351 mode_t m;
Rob Landley8a7a6782005-09-05 04:13:33 +0000352
Denis Vlasenko01a74f92006-09-23 16:34:39 +0000353 for (sct = suid_config; sct; sct = sct->m_next) {
354 if (sct->m_applet == applet)
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000355 goto found;
Denis Vlasenko01a74f92006-09-23 16:34:39 +0000356 }
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000357 /* default: drop all privileges */
358 xsetgid(rgid);
359 xsetuid(ruid);
360 return;
361 found:
362 m = sct->m_mode;
363 if (sct->m_uid == ruid)
364 /* same uid */
365 m >>= 6;
366 else if ((sct->m_gid == rgid) || ingroup(ruid, sct->m_gid))
367 /* same group / in group */
368 m >>= 3;
Rob Landley8a7a6782005-09-05 04:13:33 +0000369
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000370 if (!(m & S_IXOTH)) /* is x bit not set ? */
371 bb_error_msg_and_die("you have no permission to run this applet!");
Rob Landley8a7a6782005-09-05 04:13:33 +0000372
Denis Vlasenko3bc18252007-05-02 23:01:32 +0000373 /* _both_ sgid and group_exec have to be set for setegid */
374 if ((sct->m_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP))
375 rgid = sct->m_gid;
376 /* else (no setegid) we will set egid = rgid */
377
378 /* We set effective AND saved ids. If saved-id is not set
379 * like we do below, seteiud(0) can still later succeed! */
380 if (setresgid(-1, rgid, rgid))
381 bb_perror_msg_and_die("setresgid");
382
383 /* do we have to set effective uid? */
384 uid = ruid;
385 if (sct->m_mode & S_ISUID)
386 uid = sct->m_uid;
387 /* else (no seteuid) we will set euid = ruid */
388
389 if (setresuid(-1, uid, uid))
390 bb_perror_msg_and_die("setresuid");
Denis Vlasenko01a74f92006-09-23 16:34:39 +0000391 return;
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000392 }
393#if !ENABLE_FEATURE_SUID_CONFIG_QUIET
394 {
Bernhard Reutner-Fischer163516d2007-04-10 14:16:19 +0000395 static bool onetime = 0;
Rob Landley8a7a6782005-09-05 04:13:33 +0000396
Denis Vlasenko01a74f92006-09-23 16:34:39 +0000397 if (!onetime) {
398 onetime = 1;
Denis Vlasenko13c5a682006-10-16 22:39:51 +0000399 fprintf(stderr, "Using fallback suid method\n");
Denis Vlasenko01a74f92006-09-23 16:34:39 +0000400 }
Rob Landley8a7a6782005-09-05 04:13:33 +0000401 }
402#endif
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000403#endif
Robert Grieblc9aca452002-06-04 20:06:25 +0000404
Denis Vlasenko01a74f92006-09-23 16:34:39 +0000405 if (applet->need_suid == _BB_SUID_ALWAYS) {
Denis Vlasenko3bc18252007-05-02 23:01:32 +0000406 /* Real uid is not 0. If euid isn't 0 too, suid bit
407 * is most probably not set on our executable */
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000408 if (geteuid())
409 bb_error_msg_and_die("applet requires root privileges!");
Denis Vlasenko01a74f92006-09-23 16:34:39 +0000410 } else if (applet->need_suid == _BB_SUID_NEVER) {
Denis Vlasenko5f9468e2007-04-14 13:22:09 +0000411 xsetgid(rgid); /* drop all privileges */
Denis Vlasenko01a74f92006-09-23 16:34:39 +0000412 xsetuid(ruid);
413 }
Rob Landley8a7a6782005-09-05 04:13:33 +0000414}
415#else
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000416#define check_suid(x) ((void)0)
417#endif /* FEATURE_SUID */
Rob Landley8a7a6782005-09-05 04:13:33 +0000418
419
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000420#if ENABLE_FEATURE_COMPRESS_USAGE
Rob Landley8a7a6782005-09-05 04:13:33 +0000421
Rob Landley7e21d5f2006-04-27 23:34:46 +0000422#include "usage_compressed.h"
423#include "unarchive.h"
424
425static const char *unpack_usage_messages(void)
426{
Denis Vlasenkoc6758a02007-04-10 21:40:19 +0000427 char *outbuf = NULL;
428 bunzip_data *bd;
429 int i;
Rob Landley7e21d5f2006-04-27 23:34:46 +0000430
Denis Vlasenkoc6758a02007-04-10 21:40:19 +0000431 i = start_bunzip(&bd,
432 /* src_fd: */ -1,
433 /* inbuf: */ packed_usage,
434 /* len: */ sizeof(packed_usage));
435 /* read_bunzip can longjmp to start_bunzip, and ultimately
436 * end up here with i != 0 on read data errors! Not trivial */
437 if (!i) {
438 /* Cannot use xmalloc: will leak bd in NOFORK case! */
439 outbuf = malloc_or_warn(SIZEOF_usage_messages);
440 if (outbuf)
441 read_bunzip(bd, outbuf, SIZEOF_usage_messages);
Rob Landley7e21d5f2006-04-27 23:34:46 +0000442 }
Denis Vlasenkoc6758a02007-04-10 21:40:19 +0000443 dealloc_bunzip(bd);
444 return outbuf;
Rob Landley7e21d5f2006-04-27 23:34:46 +0000445}
Denis Vlasenkoc6758a02007-04-10 21:40:19 +0000446#define dealloc_usage_messages(s) free(s)
447
Rob Landley7e21d5f2006-04-27 23:34:46 +0000448#else
Denis Vlasenkoc6758a02007-04-10 21:40:19 +0000449
Rob Landley1801e9c2006-05-03 20:19:14 +0000450#define unpack_usage_messages() usage_messages
Denis Vlasenkoc6758a02007-04-10 21:40:19 +0000451#define dealloc_usage_messages(s) ((void)(s))
452
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000453#endif /* FEATURE_COMPRESS_USAGE */
454
Rob Landley8a7a6782005-09-05 04:13:33 +0000455
Denis Vlasenko01a74f92006-09-23 16:34:39 +0000456void bb_show_usage(void)
Rob Landley8a7a6782005-09-05 04:13:33 +0000457{
Rob Landley7e21d5f2006-04-27 23:34:46 +0000458 if (ENABLE_SHOW_USAGE) {
459 const char *format_string;
Denis Vlasenkoc6758a02007-04-10 21:40:19 +0000460 const char *p;
461 const char *usage_string = p = unpack_usage_messages();
Rob Landley7e21d5f2006-04-27 23:34:46 +0000462 int i;
Rob Landley8a7a6782005-09-05 04:13:33 +0000463
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000464 i = current_applet - applets;
465 while (i) {
Denis Vlasenkoc6758a02007-04-10 21:40:19 +0000466 while (*p++) continue;
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000467 i--;
468 }
Rob Landley7e21d5f2006-04-27 23:34:46 +0000469
Denis Vlasenkoca525b42007-06-13 12:27:17 +0000470 fprintf(stderr, "%s multi-call binary\n", bb_banner);
471 format_string = "\nUsage: %s %s\n\n";
Denis Vlasenkoc6758a02007-04-10 21:40:19 +0000472 if (*p == '\b')
Denis Vlasenkoca525b42007-06-13 12:27:17 +0000473 format_string = "\nNo help available.\n\n";
474 fprintf(stderr, format_string, applet_name, p);
Denis Vlasenkoc6758a02007-04-10 21:40:19 +0000475 dealloc_usage_messages((char*)usage_string);
Rob Landley8a7a6782005-09-05 04:13:33 +0000476 }
Denis Vlasenko335b63d2007-04-10 21:38:30 +0000477 xfunc_die();
Rob Landley8a7a6782005-09-05 04:13:33 +0000478}
479
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000480
Rob Landley53437472006-07-16 08:14:35 +0000481static int applet_name_compare(const void *name, const void *vapplet)
Rob Landley8a7a6782005-09-05 04:13:33 +0000482{
Denis Vlasenko335b63d2007-04-10 21:38:30 +0000483 const struct bb_applet *applet = vapplet;
Rob Landley8a7a6782005-09-05 04:13:33 +0000484
Denis Vlasenko01a74f92006-09-23 16:34:39 +0000485 return strcmp(name, applet->name);
Rob Landley8a7a6782005-09-05 04:13:33 +0000486}
487
Denis Vlasenko335b63d2007-04-10 21:38:30 +0000488const struct bb_applet *find_applet_by_name(const char *name)
Rob Landley8a7a6782005-09-05 04:13:33 +0000489{
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000490 /* Do a binary search to find the applet entry given the name. */
Denis Vlasenko335b63d2007-04-10 21:38:30 +0000491 return bsearch(name, applets, NUM_APPLETS, sizeof(applets[0]),
Denis Vlasenko01a74f92006-09-23 16:34:39 +0000492 applet_name_compare);
Rob Landley8a7a6782005-09-05 04:13:33 +0000493}
494
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000495
496#if ENABLE_FEATURE_INSTALLER
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000497/* create (sym)links for each applet */
498static void install_links(const char *busybox, int use_symbolic_links)
499{
Denis Vlasenko335b63d2007-04-10 21:38:30 +0000500 /* directory table
501 * this should be consistent w/ the enum,
502 * busybox.h::bb_install_loc_t, or else... */
503 static const char usr_bin [] = "/usr/bin";
504 static const char usr_sbin[] = "/usr/sbin";
505 static const char *const install_dir[] = {
506 &usr_bin [8], /* "", equivalent to "/" for concat_path_file() */
507 &usr_bin [4], /* "/bin" */
508 &usr_sbin[4], /* "/sbin" */
509 usr_bin,
510 usr_sbin
511 };
512
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000513 int (*lf)(const char *, const char *) = link;
514 char *fpc;
515 int i;
516 int rc;
517
518 if (use_symbolic_links)
519 lf = symlink;
520
521 for (i = 0; applets[i].name != NULL; i++) {
522 fpc = concat_path_file(
Denis Vlasenko335b63d2007-04-10 21:38:30 +0000523 install_dir[applets[i].install_loc],
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000524 applets[i].name);
525 rc = lf(busybox, fpc);
526 if (rc != 0 && errno != EEXIST) {
527 bb_perror_msg("%s", fpc);
528 }
529 free(fpc);
530 }
531}
532#else
533#define install_links(x,y) ((void)0)
534#endif /* FEATURE_INSTALLER */
535
536
537/* If we were called as "busybox..." */
Denis Vlasenkof5294e12007-04-14 10:09:57 +0000538static int busybox_main(char **argv)
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000539{
Denis Vlasenko2ace1e32007-04-18 21:00:21 +0000540 if (!argv[1]) {
541 /* Called without arguments */
542 const struct bb_applet *a;
543 int col, output_width;
544 help:
545 output_width = 80;
546 if (ENABLE_FEATURE_AUTOWIDTH) {
547 /* Obtain the terminal width. */
548 get_terminal_width_height(0, &output_width, NULL);
549 }
550 /* leading tab and room to wrap */
551 output_width -= sizeof("start-stop-daemon, ") + 8;
552
Denis Vlasenkoca525b42007-06-13 12:27:17 +0000553 printf("%s multi-call binary\n", bb_banner); /* reuse const string... */
554 printf("Copyright (C) 1998-2006  Erik Andersen, Rob Landley, and others.\n"
Denis Vlasenko2ace1e32007-04-18 21:00:21 +0000555 "Licensed under GPLv2.  See source distribution for full notice.\n"
556 "\n"
557 "Usage: busybox [function] [arguments]...\n"
558 " or: [function] [arguments]...\n"
559 "\n"
560 "\tBusyBox is a multi-call binary that combines many common Unix\n"
561 "\tutilities into a single executable. Most people will create a\n"
562 "\tlink to busybox for each function they wish to use and BusyBox\n"
563 "\twill act like whatever it was invoked as!\n"
Denis Vlasenkoca525b42007-06-13 12:27:17 +0000564 "\nCurrently defined functions:\n");
Denis Vlasenko2ace1e32007-04-18 21:00:21 +0000565 col = 0;
566 a = applets;
567 while (a->name) {
568 if (col > output_width) {
569 puts(",");
570 col = 0;
571 }
572 col += printf("%s%s", (col ? ", " : "\t"), a->name);
573 a++;
574 }
575 puts("\n");
576 return 0;
577 }
578
579 if (ENABLE_FEATURE_INSTALLER && strcmp(argv[1], "--install") == 0) {
Denis Vlasenkobdbbb7e2007-06-08 15:02:55 +0000580 const char *busybox;
581 busybox = xmalloc_readlink_or_warn(bb_busybox_exec_path);
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000582 if (!busybox)
Denis Vlasenkobdbbb7e2007-06-08 15:02:55 +0000583 busybox = bb_busybox_exec_path;
584 /* -s makes symlinks */
585 install_links(busybox,
586 argv[2] && strcmp(argv[2], "-s") == 0);
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000587 return 0;
588 }
589
Denis Vlasenko2ace1e32007-04-18 21:00:21 +0000590 if (strcmp(argv[1], "--help") == 0) {
591 /* "busybox --help [<applet>]" */
592 if (!argv[2])
593 goto help;
594 /* convert to "<applet> --help" */
595 argv[0] = argv[2];
596 argv[2] = NULL;
Denis Vlasenko2dfdd442007-04-09 03:29:43 +0000597 } else {
Denis Vlasenko2ace1e32007-04-18 21:00:21 +0000598 /* "busybox <applet> arg1 arg2 ..." */
599 argv++;
Denis Vlasenko2dfdd442007-04-09 03:29:43 +0000600 }
Denis Vlasenko2ace1e32007-04-18 21:00:21 +0000601 /* we want "<argv[0]>: applet not found", not "busybox: ..." */
602 applet_name = argv[0];
603 run_applet_and_exit(argv[0], argv);
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000604 bb_error_msg_and_die("applet not found");
605}
606
Denis Vlasenkof5294e12007-04-14 10:09:57 +0000607void run_current_applet_and_exit(char **argv)
Denis Vlasenkoc44ab012007-04-09 03:11:58 +0000608{
Denis Vlasenkof5294e12007-04-14 10:09:57 +0000609 int argc = 1;
610
611 while (argv[argc])
612 argc++;
613
Denis Vlasenko831a20f2007-04-12 12:27:32 +0000614 /* Reinit some shared global data */
615 optind = 1;
616 xfunc_error_retval = EXIT_FAILURE;
617
Denis Vlasenkoc44ab012007-04-09 03:11:58 +0000618 applet_name = current_applet->name;
619 if (argc == 2 && !strcmp(argv[1], "--help"))
620 bb_show_usage();
621 if (ENABLE_FEATURE_SUID)
622 check_suid(current_applet);
623 exit(current_applet->main(argc, argv));
624}
625
Denis Vlasenkof5294e12007-04-14 10:09:57 +0000626void run_applet_and_exit(const char *name, char **argv)
Rob Landley8a7a6782005-09-05 04:13:33 +0000627{
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000628 current_applet = find_applet_by_name(name);
Denis Vlasenkoc44ab012007-04-09 03:11:58 +0000629 if (current_applet)
Denis Vlasenkof5294e12007-04-14 10:09:57 +0000630 run_current_applet_and_exit(argv);
Denis Vlasenko1b6fa4c2007-03-24 12:08:36 +0000631 if (!strncmp(name, "busybox", 7))
Denis Vlasenkof5294e12007-04-14 10:09:57 +0000632 exit(busybox_main(argv));
Rob Landley8a7a6782005-09-05 04:13:33 +0000633}
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000634
635
Denis Vlasenkofad2b862007-05-31 22:16:38 +0000636#ifdef __GLIBC__
637/* Make it reside in R/W memory: */
638int *const bb_errno __attribute__ ((section (".data")));
639#endif
640
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000641int main(int argc, char **argv)
642{
643 const char *s;
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++;
659 s = strrchr(applet_name, '/');
660 if (s)
661 applet_name = s + 1;
662
Denis Vlasenko5f9468e2007-04-14 13:22:09 +0000663 parse_config_file(); /* ...maybe, if FEATURE_SUID_CONFIG */
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000664
665 /* Set locale for everybody except 'init' */
666 if (ENABLE_LOCALE_SUPPORT && getpid() != 1)
667 setlocale(LC_ALL, "");
668
Denis Vlasenkof5294e12007-04-14 10:09:57 +0000669 run_applet_and_exit(applet_name, argv);
Denis Vlasenko32b633a2007-04-09 03:05:48 +0000670 bb_error_msg_and_die("applet not found");
671}