aboutsummaryrefslogtreecommitdiff
path: root/libbb/appletlib.c
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2012-03-22 15:21:20 +0000
committerRon Yorston <rmy@pobox.com>2012-03-22 15:21:20 +0000
commit0d8b2c4a929ea9d3ac37499319fe0d8e7941a0c2 (patch)
tree6709ddd6071a9c238ba69233540bbcfe560c6a44 /libbb/appletlib.c
parent67758035a4fe040c6ac69b39d61bcd6bddd7b827 (diff)
parent56a3b82e9692a25ef9c9269e88feac0d579ce8e8 (diff)
downloadbusybox-w32-0d8b2c4a929ea9d3ac37499319fe0d8e7941a0c2.tar.gz
busybox-w32-0d8b2c4a929ea9d3ac37499319fe0d8e7941a0c2.tar.bz2
busybox-w32-0d8b2c4a929ea9d3ac37499319fe0d8e7941a0c2.zip
Merge commit '56a3b82e9692a25ef9c9269e88feac0d579ce8e8' into merge
Conflicts: coreutils/ls.c include/platform.h libbb/bb_basename.c
Diffstat (limited to 'libbb/appletlib.c')
-rw-r--r--libbb/appletlib.c213
1 files changed, 97 insertions, 116 deletions
diff --git a/libbb/appletlib.c b/libbb/appletlib.c
index a934f11bb..d4025b9c7 100644
--- a/libbb/appletlib.c
+++ b/libbb/appletlib.c
@@ -27,12 +27,13 @@
27 * FEATURE_INSTALLER or FEATURE_SUID will still link printf routines in. :( 27 * FEATURE_INSTALLER or FEATURE_SUID will still link printf routines in. :(
28 */ 28 */
29#include "busybox.h" 29#include "busybox.h"
30#include <assert.h> 30
31#if !(defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) \ 31#if !(defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) \
32 || defined(__APPLE__) \ 32 || defined(__APPLE__) \
33 ) 33 )
34# include <malloc.h> /* for mallopt */ 34# include <malloc.h> /* for mallopt */
35#endif 35#endif
36
36/* Try to pull in PAGE_SIZE */ 37/* Try to pull in PAGE_SIZE */
37#ifdef __linux__ 38#ifdef __linux__
38# include <sys/user.h> 39# include <sys/user.h>
@@ -40,6 +41,9 @@
40#ifdef __GNU__ /* Hurd */ 41#ifdef __GNU__ /* Hurd */
41# include <mach/vm_param.h> 42# include <mach/vm_param.h>
42#endif 43#endif
44#ifndef PAGE_SIZE
45# define PAGE_SIZE (4*1024) /* guess */
46#endif
43 47
44 48
45/* Declare <applet>_main() */ 49/* Declare <applet>_main() */
@@ -47,7 +51,6 @@
47#include "applets.h" 51#include "applets.h"
48#undef PROTOTYPES 52#undef PROTOTYPES
49 53
50
51/* Include generated applet names, pointers to <applet>_main, etc */ 54/* Include generated applet names, pointers to <applet>_main, etc */
52#include "applet_tables.h" 55#include "applet_tables.h"
53/* ...and if applet_tables generator says we have only one applet... */ 56/* ...and if applet_tables generator says we have only one applet... */
@@ -58,9 +61,9 @@
58# define IF_FEATURE_INDIVIDUAL(...) __VA_ARGS__ 61# define IF_FEATURE_INDIVIDUAL(...) __VA_ARGS__
59#endif 62#endif
60 63
61
62#include "usage_compressed.h" 64#include "usage_compressed.h"
63 65
66
64#if ENABLE_SHOW_USAGE && !ENABLE_FEATURE_COMPRESS_USAGE 67#if ENABLE_SHOW_USAGE && !ENABLE_FEATURE_COMPRESS_USAGE
65static const char usage_messages[] ALIGN1 = UNPACKED_USAGE; 68static const char usage_messages[] ALIGN1 = UNPACKED_USAGE;
66#else 69#else
@@ -233,13 +236,12 @@ IF_FEATURE_SUID(static uid_t ruid;) /* real uid */
233 236
234# if ENABLE_FEATURE_SUID_CONFIG 237# if ENABLE_FEATURE_SUID_CONFIG
235 238
236/* applets[] is const, so we have to define this "override" structure */ 239static struct suid_config_t {
237static struct BB_suid_config { 240 /* next ptr must be first: this struct needs to be llist-compatible */
241 struct suid_config_t *m_next;
242 struct bb_uidgid_t m_ugid;
238 int m_applet; 243 int m_applet;
239 uid_t m_uid;
240 gid_t m_gid;
241 mode_t m_mode; 244 mode_t m_mode;
242 struct BB_suid_config *m_next;
243} *suid_config; 245} *suid_config;
244 246
245static bool suid_cfg_readable; 247static bool suid_cfg_readable;
@@ -248,13 +250,10 @@ static bool suid_cfg_readable;
248static int ingroup(uid_t u, gid_t g) 250static int ingroup(uid_t u, gid_t g)
249{ 251{
250 struct group *grp = getgrgid(g); 252 struct group *grp = getgrgid(g);
251
252 if (grp) { 253 if (grp) {
253 char **mem; 254 char **mem;
254
255 for (mem = grp->gr_mem; *mem; mem++) { 255 for (mem = grp->gr_mem; *mem; mem++) {
256 struct passwd *pwd = getpwnam(*mem); 256 struct passwd *pwd = getpwnam(*mem);
257
258 if (pwd && (pwd->pw_uid == u)) 257 if (pwd && (pwd->pw_uid == u))
259 return 1; 258 return 1;
260 } 259 }
@@ -262,9 +261,7 @@ static int ingroup(uid_t u, gid_t g)
262 return 0; 261 return 0;
263} 262}
264 263
265/* This should probably be a libbb routine. In that case, 264/* libbb candidate */
266 * I'd probably rename it to something like bb_trimmed_slice.
267 */
268static char *get_trimmed_slice(char *s, char *e) 265static char *get_trimmed_slice(char *s, char *e)
269{ 266{
270 /* First, consider the value at e to be nul and back up until we 267 /* First, consider the value at e to be nul and back up until we
@@ -282,38 +279,19 @@ static char *get_trimmed_slice(char *s, char *e)
282 return skip_whitespace(s); 279 return skip_whitespace(s);
283} 280}
284 281
285/* Don't depend on the tools to combine strings. */
286static const char config_file[] ALIGN1 = "/etc/busybox.conf";
287
288/* We don't supply a value for the nul, so an index adjustment is
289 * necessary below. Also, we use unsigned short here to save some
290 * space even though these are really mode_t values. */
291static const unsigned short mode_mask[] ALIGN2 = {
292 /* SST sst xxx --- */
293 S_ISUID, S_ISUID|S_IXUSR, S_IXUSR, 0, /* user */
294 S_ISGID, S_ISGID|S_IXGRP, S_IXGRP, 0, /* group */
295 0, S_IXOTH, S_IXOTH, 0 /* other */
296};
297
298#define parse_error(x) do { errmsg = x; goto pe_label; } while (0)
299
300static void parse_config_file(void) 282static void parse_config_file(void)
301{ 283{
302 struct BB_suid_config *sct_head; 284 /* Don't depend on the tools to combine strings. */
303 struct BB_suid_config *sct; 285 static const char config_file[] ALIGN1 = "/etc/busybox.conf";
286
287 struct suid_config_t *sct_head;
304 int applet_no; 288 int applet_no;
305 FILE *f; 289 FILE *f;
306 const char *errmsg; 290 const char *errmsg;
307 char *s;
308 char *e;
309 int i;
310 unsigned lc; 291 unsigned lc;
311 smallint section; 292 smallint section;
312 char buffer[256];
313 struct stat st; 293 struct stat st;
314 294
315 assert(!suid_config); /* Should be set to NULL by bss init. */
316
317 ruid = getuid(); 295 ruid = getuid();
318 if (ruid == 0) /* run by root - don't need to even read config file */ 296 if (ruid == 0) /* run by root - don't need to even read config file */
319 return; 297 return;
@@ -322,7 +300,7 @@ static void parse_config_file(void)
322 || !S_ISREG(st.st_mode) /* Not a regular file? */ 300 || !S_ISREG(st.st_mode) /* Not a regular file? */
323 || (st.st_uid != 0) /* Not owned by root? */ 301 || (st.st_uid != 0) /* Not owned by root? */
324 || (st.st_mode & (S_IWGRP | S_IWOTH)) /* Writable by non-root? */ 302 || (st.st_mode & (S_IWGRP | S_IWOTH)) /* Writable by non-root? */
325 || !(f = fopen_for_read(config_file)) /* Cannot open? */ 303 || !(f = fopen_for_read(config_file)) /* Cannot open? */
326 ) { 304 ) {
327 return; 305 return;
328 } 306 }
@@ -332,18 +310,21 @@ static void parse_config_file(void)
332 section = lc = 0; 310 section = lc = 0;
333 311
334 while (1) { 312 while (1) {
335 s = buffer; 313 char buffer[256];
336 314 char *s;
337 if (!fgets(s, sizeof(buffer), f)) { /* Are we done? */ 315
338// why? 316 if (!fgets(buffer, sizeof(buffer), f)) { /* Are we done? */
339 if (ferror(f)) { /* Make sure it wasn't a read error. */ 317 // Looks like bloat
340 parse_error("reading"); 318 //if (ferror(f)) { /* Make sure it wasn't a read error. */
341 } 319 // errmsg = "reading";
320 // goto pe_label;
321 //}
342 fclose(f); 322 fclose(f);
343 suid_config = sct_head; /* Success, so set the pointer. */ 323 suid_config = sct_head; /* Success, so set the pointer. */
344 return; 324 return;
345 } 325 }
346 326
327 s = buffer;
347 lc++; /* Got a (partial) line. */ 328 lc++; /* Got a (partial) line. */
348 329
349 /* If a line is too long for our buffer, we consider it an error. 330 /* If a line is too long for our buffer, we consider it an error.
@@ -355,7 +336,8 @@ static void parse_config_file(void)
355 * we do err on the side of caution. Besides, the line would be 336 * we do err on the side of caution. Besides, the line would be
356 * too long if it did end with a newline. */ 337 * too long if it did end with a newline. */
357 if (!strchr(s, '\n') && !feof(f)) { 338 if (!strchr(s, '\n') && !feof(f)) {
358 parse_error("line too long"); 339 errmsg = "line too long";
340 goto pe_label;
359 } 341 }
360 342
361 /* Trim leading and trailing whitespace, ignoring comments, and 343 /* Trim leading and trailing whitespace, ignoring comments, and
@@ -371,12 +353,13 @@ static void parse_config_file(void)
371 /* Unlike the old code, we ignore leading and trailing 353 /* Unlike the old code, we ignore leading and trailing
372 * whitespace for the section name. We also require that 354 * whitespace for the section name. We also require that
373 * there are no stray characters after the closing bracket. */ 355 * there are no stray characters after the closing bracket. */
374 e = strchr(s, ']'); 356 char *e = strchr(s, ']');
375 if (!e /* Missing right bracket? */ 357 if (!e /* Missing right bracket? */
376 || e[1] /* Trailing characters? */ 358 || e[1] /* Trailing characters? */
377 || !*(s = get_trimmed_slice(s+1, e)) /* Missing name? */ 359 || !*(s = get_trimmed_slice(s+1, e)) /* Missing name? */
378 ) { 360 ) {
379 parse_error("section header"); 361 errmsg = "section header";
362 goto pe_label;
380 } 363 }
381 /* Right now we only have one section so just check it. 364 /* Right now we only have one section so just check it.
382 * If more sections are added in the future, please don't 365 * If more sections are added in the future, please don't
@@ -401,12 +384,13 @@ static void parse_config_file(void)
401 * where both key and value could contain inner whitespace. */ 384 * where both key and value could contain inner whitespace. */
402 385
403 /* First get the key (an applet name in our case). */ 386 /* First get the key (an applet name in our case). */
404 e = strchr(s, '='); 387 char *e = strchr(s, '=');
405 if (e) { 388 if (e) {
406 s = get_trimmed_slice(s, e); 389 s = get_trimmed_slice(s, e);
407 } 390 }
408 if (!e || !*s) { /* Missing '=' or empty key. */ 391 if (!e || !*s) { /* Missing '=' or empty key. */
409 parse_error("keyword"); 392 errmsg = "keyword";
393 goto pe_label;
410 } 394 }
411 395
412 /* Ok, we have an applet name. Process the rhs if this 396 /* Ok, we have an applet name. Process the rhs if this
@@ -415,13 +399,16 @@ static void parse_config_file(void)
415 * up when the busybox configuration is changed. */ 399 * up when the busybox configuration is changed. */
416 applet_no = find_applet_by_name(s); 400 applet_no = find_applet_by_name(s);
417 if (applet_no >= 0) { 401 if (applet_no >= 0) {
402 unsigned i;
403 struct suid_config_t *sct;
404
418 /* Note: We currently don't check for duplicates! 405 /* Note: We currently don't check for duplicates!
419 * The last config line for each applet will be the 406 * The last config line for each applet will be the
420 * one used since we insert at the head of the list. 407 * one used since we insert at the head of the list.
421 * I suppose this could be considered a feature. */ 408 * I suppose this could be considered a feature. */
422 sct = xmalloc(sizeof(struct BB_suid_config)); 409 sct = xzalloc(sizeof(*sct));
423 sct->m_applet = applet_no; 410 sct->m_applet = applet_no;
424 sct->m_mode = 0; 411 /*sct->m_mode = 0;*/
425 sct->m_next = sct_head; 412 sct->m_next = sct_head;
426 sct_head = sct; 413 sct_head = sct;
427 414
@@ -430,48 +417,41 @@ static void parse_config_file(void)
430 e = skip_whitespace(e+1); 417 e = skip_whitespace(e+1);
431 418
432 for (i = 0; i < 3; i++) { 419 for (i = 0; i < 3; i++) {
433 /* There are 4 chars + 1 nul for each of user/group/other. */ 420 /* There are 4 chars for each of user/group/other.
434 static const char mode_chars[] ALIGN1 = "Ssx-\0" "Ssx-\0" "Ttx-"; 421 * "x-xx" instead of "x-" are to make
435 422 * "idx > 3" check catch invalid chars.
436 const char *q; 423 */
437 q = strchrnul(mode_chars + 5*i, *e++); 424 static const char mode_chars[] ALIGN1 = "Ssx-" "Ssx-" "x-xx";
438 if (!*q) { 425 static const unsigned short mode_mask[] ALIGN2 = {
439 parse_error("mode"); 426 S_ISUID, S_ISUID|S_IXUSR, S_IXUSR, 0, /* Ssx- */
427 S_ISGID, S_ISGID|S_IXGRP, S_IXGRP, 0, /* Ssx- */
428 S_IXOTH, 0 /* x- */
429 };
430 const char *q = strchrnul(mode_chars + 4*i, *e);
431 unsigned idx = q - (mode_chars + 4*i);
432 if (idx > 3) {
433 errmsg = "mode";
434 goto pe_label;
440 } 435 }
441 /* Adjust by -i to account for nul. */ 436 sct->m_mode |= mode_mask[q - mode_chars];
442 sct->m_mode |= mode_mask[(q - mode_chars) - i]; 437 e++;
443 } 438 }
444 439
445 /* Now get the user/group info. */ 440 /* Now get the user/group info. */
446 441
447 s = skip_whitespace(e); 442 s = skip_whitespace(e);
448 443 /* Default is 0.0, else parse USER.GROUP: */
449 /* Note: we require whitespace between the mode and the 444 if (*s) {
450 * user/group info. */ 445 /* We require whitespace between mode and USER.GROUP */
451 if ((s == e) || !(e = strchr(s, '.'))) { 446 if ((s == e) || !(e = strchr(s, '.'))) {
452 parse_error("<uid>.<gid>"); 447 errmsg = "uid.gid";
453 } 448 goto pe_label;
454 *e++ = '\0';
455
456 /* We can't use get_ug_id here since it would exit()
457 * if a uid or gid was not found. Oh well... */
458 sct->m_uid = bb_strtoul(s, NULL, 10);
459 if (errno) {
460 struct passwd *pwd = getpwnam(s);
461 if (!pwd) {
462 parse_error("user");
463 } 449 }
464 sct->m_uid = pwd->pw_uid; 450 *e = ':'; /* get_uidgid needs USER:GROUP syntax */
465 } 451 if (get_uidgid(&sct->m_ugid, s, /*allow_numeric:*/ 1) == 0) {
466 452 errmsg = "unknown user/group";
467 sct->m_gid = bb_strtoul(e, NULL, 10); 453 goto pe_label;
468 if (errno) {
469 struct group *grp;
470 grp = getgrnam(e);
471 if (!grp) {
472 parse_error("group");
473 } 454 }
474 sct->m_gid = grp->gr_gid;
475 } 455 }
476 } 456 }
477 continue; 457 continue;
@@ -485,22 +465,18 @@ static void parse_config_file(void)
485 * We may want to simply ignore such lines in case they 465 * We may want to simply ignore such lines in case they
486 * are used in some future version of busybox. */ 466 * are used in some future version of busybox. */
487 if (!section) { 467 if (!section) {
488 parse_error("keyword outside section"); 468 errmsg = "keyword outside section";
469 goto pe_label;
489 } 470 }
490 471
491 } /* while (1) */ 472 } /* while (1) */
492 473
493 pe_label: 474 pe_label:
494 fprintf(stderr, "Parse error in %s, line %d: %s\n",
495 config_file, lc, errmsg);
496
497 fclose(f); 475 fclose(f);
476 bb_error_msg("parse error in %s, line %u: %s", config_file, lc, errmsg);
477
498 /* Release any allocated memory before returning. */ 478 /* Release any allocated memory before returning. */
499 while (sct_head) { 479 llist_free((llist_t*)sct_head, NULL);
500 sct = sct_head->m_next;
501 free(sct_head);
502 sct_head = sct;
503 }
504} 480}
505# else 481# else
506static inline void parse_config_file(void) 482static inline void parse_config_file(void)
@@ -522,7 +498,7 @@ static void check_suid(int applet_no)
522# if ENABLE_FEATURE_SUID_CONFIG 498# if ENABLE_FEATURE_SUID_CONFIG
523 if (suid_cfg_readable) { 499 if (suid_cfg_readable) {
524 uid_t uid; 500 uid_t uid;
525 struct BB_suid_config *sct; 501 struct suid_config_t *sct;
526 mode_t m; 502 mode_t m;
527 503
528 for (sct = suid_config; sct; sct = sct->m_next) { 504 for (sct = suid_config; sct; sct = sct->m_next) {
@@ -531,36 +507,40 @@ static void check_suid(int applet_no)
531 } 507 }
532 goto check_need_suid; 508 goto check_need_suid;
533 found: 509 found:
510 /* Is this user allowed to run this applet? */
534 m = sct->m_mode; 511 m = sct->m_mode;
535 if (sct->m_uid == ruid) 512 if (sct->m_ugid.uid == ruid)
536 /* same uid */ 513 /* same uid */
537 m >>= 6; 514 m >>= 6;
538 else if ((sct->m_gid == rgid) || ingroup(ruid, sct->m_gid)) 515 else if ((sct->m_ugid.gid == rgid) || ingroup(ruid, sct->m_ugid.gid))
539 /* same group / in group */ 516 /* same group / in group */
540 m >>= 3; 517 m >>= 3;
541 518 if (!(m & S_IXOTH)) /* is x bit not set? */
542 if (!(m & S_IXOTH)) /* is x bit not set ? */ 519 bb_error_msg_and_die("you have no permission to run this applet");
543 bb_error_msg_and_die("you have no permission to run this applet!");
544
545 /* _both_ sgid and group_exec have to be set for setegid */
546 if ((sct->m_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP))
547 rgid = sct->m_gid;
548 /* else (no setegid) we will set egid = rgid */
549 520
550 /* We set effective AND saved ids. If saved-id is not set 521 /* We set effective AND saved ids. If saved-id is not set
551 * like we do below, seteiud(0) can still later succeed! */ 522 * like we do below, seteuid(0) can still later succeed! */
523
524 /* Are we directed to change gid
525 * (APPLET = *s* USER.GROUP or APPLET = *S* USER.GROUP)?
526 */
527 if (sct->m_mode & S_ISGID)
528 rgid = sct->m_ugid.gid;
529 /* else: we will set egid = rgid, thus dropping sgid effect */
552 if (setresgid(-1, rgid, rgid)) 530 if (setresgid(-1, rgid, rgid))
553 bb_perror_msg_and_die("setresgid"); 531 bb_perror_msg_and_die("setresgid");
554 532
555 /* do we have to set effective uid? */ 533 /* Are we directed to change uid
534 * (APPLET = s** USER.GROUP or APPLET = S** USER.GROUP)?
535 */
556 uid = ruid; 536 uid = ruid;
557 if (sct->m_mode & S_ISUID) 537 if (sct->m_mode & S_ISUID)
558 uid = sct->m_uid; 538 uid = sct->m_ugid.uid;
559 /* else (no seteuid) we will set euid = ruid */ 539 /* else: we will set euid = ruid, thus dropping suid effect */
560
561 if (setresuid(-1, uid, uid)) 540 if (setresuid(-1, uid, uid))
562 bb_perror_msg_and_die("setresuid"); 541 bb_perror_msg_and_die("setresuid");
563 return; 542
543 goto ret;
564 } 544 }
565# if !ENABLE_FEATURE_SUID_CONFIG_QUIET 545# if !ENABLE_FEATURE_SUID_CONFIG_QUIET
566 { 546 {
@@ -568,7 +548,7 @@ static void check_suid(int applet_no)
568 548
569 if (!onetime) { 549 if (!onetime) {
570 onetime = 1; 550 onetime = 1;
571 fprintf(stderr, "Using fallback suid method\n"); 551 bb_error_msg("using fallback suid method");
572 } 552 }
573 } 553 }
574# endif 554# endif
@@ -583,6 +563,10 @@ static void check_suid(int applet_no)
583 xsetgid(rgid); /* drop all privileges */ 563 xsetgid(rgid); /* drop all privileges */
584 xsetuid(ruid); 564 xsetuid(ruid);
585 } 565 }
566# if ENABLE_FEATURE_SUID_CONFIG
567 ret: ;
568 llist_free((llist_t*)suid_config, NULL);
569# endif
586} 570}
587# else 571# else
588# define check_suid(x) ((void)0) 572# define check_suid(x) ((void)0)
@@ -799,9 +783,6 @@ int main(int argc UNUSED_PARAM, char **argv)
799#endif 783#endif
800{ 784{
801 /* Tweak malloc for reduced memory consumption */ 785 /* Tweak malloc for reduced memory consumption */
802#ifndef PAGE_SIZE
803# define PAGE_SIZE (4*1024) /* guess */
804#endif
805#ifdef M_TRIM_THRESHOLD 786#ifdef M_TRIM_THRESHOLD
806 /* M_TRIM_THRESHOLD is the maximum amount of freed top-most memory 787 /* M_TRIM_THRESHOLD is the maximum amount of freed top-most memory
807 * to keep before releasing to the OS 788 * to keep before releasing to the OS