[prev in list] [next in list] [prev in thread] [next in thread] 

List:       busybox
Subject:    [BusyBox] Command line parsing bug in adduser.c of busybox-unstable?
From:       tito <farmatito () tiscali ! it>
Date:       2002-06-28 6:43:04
[Download RAW message or body]

Hi to everybody,
I'm writing to report a command line parsing bug in adduser.c of 
busybox-unstable,which is also present in latest CVS I've downloaded 
yesterday.
What happens is:

 if using a command line like:
./adduser -h /home/pippo -s /bin/bash -g Microlinux  pippo

you get:

Segmentation fault (core dumped)
root@localhost.localdomain:/dev/pts/1:/rep/bbbox_unstable/busybox#

and in /etc/passwd

pippo:x:506:506:-s:-h:/home/pippo

That's bad 'cause I was so happy about new busybox making me save 14Kb by 
integrating tinylogin on my linux on a floppy system : (. 
So I tried to catch the bug ( by the way  I'm  just at Chapter 3 of my C 
programming manual....so have mercy) and put printf()'s in the whole 
adduser.c.
What I get was this:

# ./adduser -h /home/pippo -s /bin/bash -g Microlinux  pippo
argc 8
argv0 ./adduser
argv1 -h
argv2 /home/pippo
argv3 -s
argv4 /bin/bash
argv5 -g
argv6 Microlinux
argv7 pippo
argv8 (null)
home -h
gecos -s
shell /home/pippo
login pippo
Segmentation fault (core dumped)
root@localhost.localdomain:/dev/pts/1:/rep/bbbox_unstable/busybox# 

It seemed to me something was wrong with the command line parsing.
I tried  to debug it but at the moment I'm not knowing  enough about getopt();
so after a while I rewrote it completely with functions I'm more at ease with.
I know my code is ugly and not optimized at all so it is just an input for 
the busybox programming gurus to fix it more elegantly.........;-))

Sorry ,I don't know exactly how diff's work so here is the whole code as 
attachment togheter with my .config file.

The changes I've made fix the command line parsing, but before the program 
exits it shows up an usage message.
I spent half a sleepless night to track this but I was not able to do it.
By my sixt sense I'would say that maybe the bug should not be in adduser.c 
itself, but I really dont know.

What I get now is:

# ./adduser -h /home/pippo -s /bin/bash -g Microlinux  pippo
argc 8
argv0 ./adduser
argv1 -h
argv2 /home/pippo
argv3 -s
argv4 /bin/bash
argv5 -g
argv6 Microlinux
argv7 pippo
argv8 (null)
int i = 8
int a = 5
home /home/pippo
gecos Microlinux
shell /bin/bash
login pippo
BusyBox v0.61.pre (2002.06.27-21:20+0000) multi-call binary

Usage: adduser [OPTIONS] <user_name>

root@localhost.localdomain:/dev/pts/1:/rep/bbbox_unstable/busybox# 

with the correct entry  written in /etc/passwd.


Best regards and Ciao to all

Tito
["adduser_new.c" (text/x-c)]

/* vi: set sw=4 ts=4: */
/*
 * adduser - add users to /etc/passwd and /etc/shadow
 *
 * Copyright (C) 1999 by Lineo, inc. and John Beppu
 * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 */

#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "busybox.h"



/* structs __________________________ */

typedef struct {
	uid_t u;
	gid_t g;
} Id;

/* data _____________________________ */

/* defaults : should this be in an external file? */
static const char default_passwd[] = "x";
static const char default_gecos[] = "Linux User,,,";
static const char default_home_prefix[] = "/home";
static const char default_shell[] = "/bin/sh";

#ifdef CONFIG_FEATURE_SHADOWPASSWDS
/* shadow in use? */
static int shadow_enabled = 0;
#endif

/* remix */
/* EDR recoded such that the uid may be passed in *p */
static int passwd_study(const char *filename, struct passwd *p)
{
	struct passwd *pw;
	FILE *passwd;

	const int min = 500;
	const int max = 65000;

	passwd = wfopen(filename, "r");
	if (!passwd)
		return 4;

	/* EDR if uid is out of bounds, set to min */
	if ((p->pw_uid > max) || (p->pw_uid < min))
		p->pw_uid = min;

	/* stuff to do:  
	 * make sure login isn't taken;
	 * find free uid and gid;
	 */
	while ((pw = fgetpwent(passwd))) {
		if (strcmp(pw->pw_name, p->pw_name) == 0) {
			/* return 0; */
			return 1;
		}
		if ((pw->pw_uid >= p->pw_uid) && (pw->pw_uid < max)
			&& (pw->pw_uid >= min)) {
			p->pw_uid = pw->pw_uid + 1;
		}
	}

	/* EDR check for an already existing gid */
	while (getgrgid(p->pw_uid) != NULL)
		p->pw_uid++;

	/* EDR also check for an existing group definition */
	if (getgrnam(p->pw_name) != NULL)
		return 3;

	/* EDR bounds check */
	if ((p->pw_uid > max) || (p->pw_uid < min))
		return 2;

	/* EDR create new gid always = uid */
	p->pw_gid = p->pw_uid;

	/* return 1; */
	return 0;
}

static void addgroup_wrapper(const char *login, gid_t gid)
{
	int argc = 3;
	const char *argv0_save;
	char group_id[8];
	char group_name[32];
	char *argv[] = { group_name, "-g", group_id };

	argv0_save = applet_name;
	applet_name = "addgroup";
	safe_strncpy(group_name, login, 32);
	sprintf(group_id, "%d", gid);
	addgroup_main(argc, argv);
	applet_name = argv0_save;
}

static void passwd_wrapper(const char *login)
{
	static const char prog[] = "passwd";
	execlp(prog, prog, login, NULL);
	error_msg_and_die("Failed to execute 'passwd', you must set the password for '%s' manually", login);
}

/* putpwent(3) remix */
static int adduser(const char *filename, struct passwd *p)
{
	FILE *passwd;
	int r;
#ifdef CONFIG_FEATURE_SHADOWPASSWDS
	FILE *shadow;
	struct spwd *sp;
#endif

	/* make sure everything is kosher and setup uid && gid */
	passwd = wfopen(filename, "a");
	if (passwd == NULL) {
		/* return -1; */
		return 1;
	}
	fseek(passwd, 0, SEEK_END);

	/* if (passwd_study(filename, p) == 0) { */
	r = passwd_study(filename, p);
	if (r) {
		if (r == 1)
			error_msg("%s: login already in use", p->pw_name);
		else if (r == 2)
			error_msg("illegal uid or no uids left");
		else if (r == 3)
			error_msg("group name %s already in use", p->pw_name);
		else
			error_msg("generic error.");
		/* return -1; */
		return 1;
	}

	/* add to passwd */
	if (putpwent(p, passwd) == -1) {
		/* return -1; */
		return 1;
	}
	fclose(passwd);

#ifdef CONFIG_FEATURE_SHADOWPASSWDS
	/* add to shadow if necessary */
	if (shadow_enabled) {
		shadow = wfopen(shadow_file, "a");
		if (shadow == NULL) {
			/* return -1; */
			return 1;
		}
		fseek(shadow, 0, SEEK_END);
		sp = pwd_to_spwd(p);
		sp->sp_max = 99999;		/* debianish */
		sp->sp_warn = 7;
		fprintf(shadow, "%s:!:%ld:%ld:%ld:%ld:::\n",
				sp->sp_namp, sp->sp_lstchg, sp->sp_min, sp->sp_max,
				sp->sp_warn);
		fclose(shadow);
	}
#endif

	/* add to group */
	/* addgroup should be responsible for dealing w/ gshadow */
	addgroup_wrapper(p->pw_name, p->pw_gid);

	/* Clear the umask for this process so it doesn't
	 * * screw up the permissions on the mkdir and chown. */
	umask(0);

	/* mkdir */
	if (mkdir(p->pw_dir, 0755)) {
		perror_msg("%s", p->pw_dir);
	}
	/* Set the owner and group so it is owned by the new user. */
	if (chown(p->pw_dir, p->pw_uid, p->pw_gid)) {
		perror_msg("%s", p->pw_dir);
	}
	/* Now fix up the permissions to 2755. Can't do it before now
	 * since chown will clear the setgid bit */
	if (chmod(p->pw_dir, 02755)) {
		perror_msg("%s", p->pw_dir);
	}
	/* interactively set passwd */
	passwd_wrapper(p->pw_name);

	return 0;
}


/* return current uid (root is always uid == 0, right?) */
static inline uid_t i_am_not_root(void)
{
	return geteuid();
}

/*
 * adduser will take a login_name as its first parameter.
 *
 * home
 * shell
 * gecos 
 *
 * can be customized via command-line parameters.
 * ________________________________________________________________________ */
int adduser_main(int argc, char **argv)
{
	int i = 0;
	int a = 0;
	/*char opt;*/
	const char *login;
	const char *gecos;
	const char *home = NULL;
	const char *shell;

	struct passwd pw;

	/* init */
	if (argc < 2 ) {
		show_usage();
	}
	gecos = default_gecos;
	shell = default_shell;

	/* get args */
	for(i=1; i<argc ;i++)
		{
		if(strcasecmp(argv[i],"-h")==0)
        	{
			a = i;
			i = i + 1;
			if(argv[i]==NULL || *argv[i]=='-')
        		{
				error_msg_and_die( "Missing argument.");
			}
			home = argv[i];
		}
		if(strcasecmp(argv[i],"-g")==0)
        	{
			a = i;
			i = i + 1;
			if(argv[i]==NULL || *argv[i]=='-')
        		{
				error_msg_and_die( "Missing argument.");
			}
			gecos = argv[i];
        	}
		if(strcasecmp(argv[i],"-s")==0)
        	{
			a = i;
			i = i + 1;
			if(argv[i]==NULL || *argv[i]=='-')
        		{
				error_msg_and_die( "Missing argument.");
			}
			shell = argv[i];
        	}
	}
	/*while ((opt = getopt (argc, argv, "h:g:s:")) != -1)
		switch (opt) {
			case 'h':
				home = argv[i];
				break;
			case 'g':
				gecos = argv[i];
				break;
			case 's':
				shell = argv[i];
				break;
			default:
				show_usage ();
				break;
		}*/
	/* got root? */
	if (i_am_not_root()) {
		error_msg_and_die( "Only root may add a user or group to the system.");
	}

	/* get login */
	/*if (optind >= argc) {
		error_msg_and_die( "no user specified");
	}*/


	if (a == 0 ||a == (argc - 3  ))
		{
		login = argv[argc - 1];
		printf("login %s\n",login);   /*debug*/
		}
	else
		error_msg_and_die( "no user specified");

	/* create string for $HOME if not specified already */
	if (!home) {
		home = concat_path_file(default_home_prefix, login);
	}
#ifdef CONFIG_FEATURE_SHADOWPASSWDS
	/* is /etc/shadow in use? */
	shadow_enabled = (0 == access(shadow_file, F_OK));
#endif

	/* create a passwd struct */
	pw.pw_name = (char *)login;
	pw.pw_passwd = (char *)default_passwd;
	pw.pw_uid = 0;
	pw.pw_gid = 0;
	pw.pw_gecos = (char *)gecos;
	pw.pw_dir = (char *)home;
	pw.pw_shell = (char *)shell;

	/* grand finale */
	return adduser(passwd_file, &pw);
}

/* $Id: adduser.c,v 1.2 2002/06/23 04:24:24 andersen Exp $ */

["config.txt" (text/plain)]

#
# Automatically generated make config: don't edit
#

#
# BusyBox general settings
#
# CONFIG_FEATURE_BUFFERS_USE_MALLOC is not set
CONFIG_FEATURE_BUFFERS_GO_ON_STACK=y
# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set
# CONFIG_FEATURE_VERBOSE_USAGE is not set
# CONFIG_FEATURE_INSTALLER is not set
# CONFIG_LOCALE_SUPPORT is not set
# CONFIG_FEATURE_DEVFS is not set
# CONFIG_FEATURE_UNCOMPRESS is not set
# CONFIG_FEATURE_CLEAN_UP is not set
# CONFIG_FEATURE_SUID is not set
CONFIG_USE_BB_PWD_GRP=y
# CONFIG_USE_BB_SHADOW is not set

#
# Archival Utilities
#
CONFIG_AR=y
# CONFIG_BUNZIP2 is not set
CONFIG_CPIO=y
# CONFIG_DPKG is not set
# CONFIG_DPKG_DEB is not set
# CONFIG_GUNZIP is not set
# CONFIG_GZIP is not set
# CONFIG_RPM2CPIO is not set
# CONFIG_TAR is not set
# CONFIG_FEATURE_UNARCHIVE_TAPE is not set
# CONFIG_UNZIP is not set

#
# Console Utilities
#
# CONFIG_CHVT is not set
CONFIG_CLEAR=y
# CONFIG_DEALLOCVT is not set
# CONFIG_DUMPKMAP is not set
# CONFIG_LOADACM is not set
# CONFIG_LOADFONT is not set
# CONFIG_LOADKMAP is not set
CONFIG_RESET=y
# CONFIG_SETKEYCODES is not set

#
# Editors
#
# CONFIG_SED is not set
# CONFIG_VI is not set

#
# File Utilities
#
CONFIG_CHMOD=y
CONFIG_CHOWN=y
CONFIG_CHGRP=y
CONFIG_CP=y
CONFIG_DD=y
CONFIG_DF=y
CONFIG_DU=y
# CONFIG_LN is not set
# CONFIG_LS is not set
CONFIG_MKDIR=y
CONFIG_MKFIFO=y
CONFIG_MKNOD=y
CONFIG_MV=y
# CONFIG_RM is not set
# CONFIG_RMDIR is not set
CONFIG_SYNC=y
CONFIG_TOUCH=y

#
# Common options for cp and mv
#
CONFIG_FEATURE_PRESERVE_HARDLINKS=y

#
# Common options for df, du, ls
#
CONFIG_FEATURE_HUMAN_READABLE=y

#
# Finding Utilities
#
CONFIG_FIND=y
CONFIG_FEATURE_FIND_MTIME=y
CONFIG_FEATURE_FIND_PERM=y
CONFIG_FEATURE_FIND_TYPE=y
# CONFIG_GREP is not set
CONFIG_WHICH=y
# CONFIG_XARGS is not set

#
# Init Utilities
#
# CONFIG_INIT is not set
# CONFIG_START_STOP_DAEMON is not set
# CONFIG_RUN_PARTS is not set

#
# Miscellaneous Utilities
#
# CONFIG_ADJTIMEX is not set
# CONFIG_DC is not set
# CONFIG_DUTMP is not set
CONFIG_MAKEDEVS=y
CONFIG_MKTEMP=y
# CONFIG_MT is not set
CONFIG_READLINK=y
# CONFIG_TIME is not set
CONFIG_UPDATE=y
# CONFIG_WATCHDOG is not set

#
# Module Utilities
#
# CONFIG_INSMOD is not set
# CONFIG_LSMOD is not set
# CONFIG_MODPROBE is not set
# CONFIG_RMMOD is not set

#
# Networking Utilities
#
# CONFIG_HOSTNAME is not set
# CONFIG_IFCONFIG is not set
# CONFIG_NC is not set
# CONFIG_NETSTAT is not set
# CONFIG_NSLOOKUP is not set
# CONFIG_PING is not set
# CONFIG_ROUTE is not set
# CONFIG_TELNET is not set
# CONFIG_TFTP is not set
# CONFIG_TRACEROUTE is not set
# CONFIG_WGET is not set

#
# Login/Password Management Utilities
#
CONFIG_ADDGROUP=y
CONFIG_ADDUSER=y
CONFIG_DELUSER=y
CONFIG_DELUSER=y
# CONFIG_GETTY is not set
# CONFIG_LOGIN is not set
# CONFIG_PASSWD is not set
CONFIG_SU=y
# CONFIG_FEATURE_SHADOWPASSWDS is not set
# CONFIG_SULOGIN is not set
# CONFIG_VLOCK is not set

#
# Process Utilities
#
# CONFIG_FREE is not set
# CONFIG_KILL is not set
# CONFIG_KILLALL is not set
# CONFIG_PIDOF is not set
# CONFIG_PS is not set
CONFIG_RENICE=y
# CONFIG_UPTIME is not set

#
# Bourne Shell
#
# CONFIG_FEATURE_SH_IS_ASH is not set
# CONFIG_FEATURE_SH_IS_HUSH is not set
# CONFIG_FEATURE_SH_IS_LASH is not set
# CONFIG_FEATURE_SH_IS_MSH is not set
CONFIG_FEATURE_SH_IS_NONE=y
# CONFIG_ASH is not set
# CONFIG_HUSH is not set
# CONFIG_LASH is not set
# CONFIG_MSH is not set

#
# Bourne Shell Options
#
# CONFIG_FEATURE_COMMAND_EDITING is not set
# CONFIG_FEATURE_COMMAND_TAB_COMPLETION is not set
# CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION is not set
# CONFIG_FEATURE_SH_STANDALONE_SHELL is not set
# CONFIG_FEATURE_SH_APPLETS_ALWAYS_WIN is not set
# CONFIG_FEATURE_SH_FANCY_PROMPT is not set
# CONFIG_FEATURE_SH_EXTRA_QUIET is not set

#
# Shell Utilities
#
# CONFIG_BASENAME is not set
# CONFIG_CAL is not set
CONFIG_CHROOT=y
CONFIG_DATE=y
# CONFIG_DIRNAME is not set
# CONFIG_ECHO is not set
# CONFIG_ENV is not set
CONFIG_EXPR=y
CONFIG_FALSE=y
# CONFIG_HOSTID is not set
CONFIG_ID=y
CONFIG_LOGNAME=y
# CONFIG_PRINTF is not set
# CONFIG_PWD is not set
CONFIG_SLEEP=y
CONFIG_STTY=y
CONFIG_TEE=y
# CONFIG_TEST is not set
CONFIG_TRUE=y
CONFIG_TTY=y
# CONFIG_UNAME is not set
# CONFIG_USLEEP is not set
# CONFIG_WHO is not set
# CONFIG_WHOAMI is not set
# CONFIG_YES is not set

#
# System Logging Utilities
#
# CONFIG_KLOGD is not set
# CONFIG_LOGGER is not set
CONFIG_SYSLOGD=y
CONFIG_FEATURE_REMOTE_LOG=y
CONFIG_FEATURE_IPC_SYSLOG=y
CONFIG_LOGREAD=y

#
# Text Utilities
#
# CONFIG_CAT is not set
CONFIG_CMP=y
CONFIG_CUT=y
# CONFIG_DOS2UNIX is not set
CONFIG_HEAD=y
CONFIG_LENGTH=y
CONFIG_MD5SUM=y
# CONFIG_OD is not set
CONFIG_SORT=y
CONFIG_FEATURE_SORT_REVERSE=y
CONFIG_FEATURE_SORT_UNIQUE=y
CONFIG_TAIL=y
CONFIG_FEATURE_FANCY_TAIL=y
# CONFIG_TR is not set
CONFIG_UNIQ=y
# CONFIG_DOS2UNIX is not set
# CONFIG_UUDECODE is not set
# CONFIG_UUENCODE is not set
CONFIG_WC=y

#
# Linux System Utilities
#
CONFIG_DMESG=y
# CONFIG_FBSET is not set
# CONFIG_FDFLUSH is not set
CONFIG_FREERAMDISK=y
# CONFIG_FSCK_MINIX is not set
# CONFIG_MKFS_MINIX is not set
# CONFIG_GETOPT is not set
# CONFIG_HEXDUMP is not set
# CONFIG_LOSETUP is not set
# CONFIG_MKSWAP is not set
# CONFIG_MORE is not set
# CONFIG_PIVOT_ROOT is not set
# CONFIG_RDATE is not set
# CONFIG_SWAPONOFF is not set
# CONFIG_MOUNT is not set
# CONFIG_UMOUNT is not set



[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic