Check one and only one of start, stop are given.
[oweals/busybox.git] / loginutils / adduser.c
index 66fcaa23f110d51b373c1805f2cd43bc541ce069..c4ab557d010caf39ea79a42617ae5f5a68a73698 100644 (file)
@@ -21,6 +21,9 @@
  *
  */
 
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
 #include <errno.h>
 #include <fcntl.h>
 #include <stdarg.h>
 #include <string.h>
 #include <time.h>
 #include <unistd.h>
+#include <getopt.h>
 #include <sys/param.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include "busybox.h"
-#include "pwd.h"
-#include "grp.h"
 
-#define PASSWD_FILE     "/etc/passwd"
-#define SHADOW_FILE            "/etc/shadow"
 
 
 /* structs __________________________ */
@@ -53,12 +53,8 @@ typedef struct {
 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
-
-#include "shadow.h"
-
 /* shadow in use? */
 static int shadow_enabled = 0;
 #endif
@@ -73,7 +69,7 @@ static int passwd_study(const char *filename, struct passwd *p)
        const int min = 500;
        const int max = 65000;
 
-       passwd = wfopen(filename, "r");
+       passwd = bb_wfopen(filename, "r");
        if (!passwd)
                return 4;
 
@@ -96,91 +92,47 @@ static int passwd_study(const char *filename, struct passwd *p)
                }
        }
 
-       /* EDR check for an already existing gid */
-       while (getgrgid(p->pw_uid) != NULL)
-               p->pw_uid++;
+       if (p->pw_gid == 0) {
+               /* 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 also check for an existing group definition */
+               if (getgrnam(p->pw_name) != NULL)
+                       return 3;
+
+               /* EDR create new gid always = uid */
+               p->pw_gid = p->pw_uid;
+       }
 
        /* 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;
+       char *cmd;
+
+       bb_xasprintf(&cmd, "addgroup -g %d %s", gid, login);
+       system(cmd);
+       free(cmd);
 }
 
+static void passwd_wrapper(const char *login) __attribute__ ((noreturn));
+
 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);
-}
-
-#ifdef CONFIG_FEATURE_SHADOWPASSWDS
-/*
- * pwd_to_spwd - create entries for new spwd structure
- *
- *     pwd_to_spwd() creates a new (struct spwd) containing the
- *     information in the pointed-to (struct passwd).
- */
-#define DAY (24L*3600L)
-#define WEEK (7*DAY)
-#define SCALE DAY
-static struct spwd *pwd_to_spwd(const struct passwd *pw)
-{
-       static struct spwd sp;
-
-       /*
-        * Nice, easy parts first.  The name and passwd map directly
-        * from the old password structure to the new one.
-        */
-       sp.sp_namp = pw->pw_name;
-       sp.sp_pwdp = pw->pw_passwd;
-
-       /*
-        * Defaults used if there is no pw_age information.
-        */
-       sp.sp_min = 0;
-       sp.sp_max = (10000L * DAY) / SCALE;
-       sp.sp_lstchg = time((time_t *) 0) / SCALE;
-
-       /*
-        * These fields have no corresponding information in the password
-        * file.  They are set to uninitialized values.
-        */
-       sp.sp_warn = -1;
-       sp.sp_expire = -1;
-       sp.sp_inact = -1;
-       sp.sp_flag = -1;
-
-       return &sp;
+       bb_error_msg_and_die("Failed to execute '%s', you must set the password for '%s' manually", prog, login);
 }
-#endif
 
 /* putpwent(3) remix */
-static int adduser(const char *filename, struct passwd *p)
+static int adduser(const char *filename, struct passwd *p, int makehome, int setpass)
 {
        FILE *passwd;
        int r;
@@ -188,11 +140,15 @@ static int adduser(const char *filename, struct passwd *p)
        FILE *shadow;
        struct spwd *sp;
 #endif
+       int new_group = 1;
+
+       /* if using a pre-existing group, don't create one */
+       if (p->pw_gid != 0)
+               new_group = 0;
 
        /* make sure everything is kosher and setup uid && gid */
-       passwd = wfopen(filename, "a");
+       passwd = bb_wfopen(filename, "a");
        if (passwd == NULL) {
-               /* return -1; */
                return 1;
        }
        fseek(passwd, 0, SEEK_END);
@@ -201,20 +157,18 @@ static int adduser(const char *filename, struct passwd *p)
        r = passwd_study(filename, p);
        if (r) {
                if (r == 1)
-                       error_msg("%s: login already in use", p->pw_name);
+                       bb_error_msg("%s: login already in use", p->pw_name);
                else if (r == 2)
-                       error_msg("illegal uid or no uids left");
+                       bb_error_msg("illegal uid or no uids left");
                else if (r == 3)
-                       error_msg("group name %s already in use", p->pw_name);
+                       bb_error_msg("group name %s already in use", p->pw_name);
                else
-                       error_msg("generic error.");
-               /* return -1; */
+                       bb_error_msg("generic error.");
                return 1;
        }
 
        /* add to passwd */
        if (putpwent(p, passwd) == -1) {
-               /* return -1; */
                return 1;
        }
        fclose(passwd);
@@ -222,9 +176,8 @@ static int adduser(const char *filename, struct passwd *p)
 #ifdef CONFIG_FEATURE_SHADOWPASSWDS
        /* add to shadow if necessary */
        if (shadow_enabled) {
-               shadow = wfopen(SHADOW_FILE, "a");
+               shadow = bb_wfopen(bb_path_shadow_file, "a");
                if (shadow == NULL) {
-                       /* return -1; */
                        return 1;
                }
                fseek(shadow, 0, SEEK_END);
@@ -238,40 +191,56 @@ static int adduser(const char *filename, struct passwd *p)
        }
 #endif
 
-       /* add to group */
-       /* addgroup should be responsible for dealing w/ gshadow */
-       addgroup_wrapper(p->pw_name, p->pw_gid);
+       if (new_group) {
+               /* 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);
+       if (makehome) {
+               /* mkdir */
+               if (mkdir(p->pw_dir, 0755)) {
+                       bb_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)) {
+                       bb_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)) {
+                       bb_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);
+
+       if (setpass) {
+               /* interactively set passwd */
+               passwd_wrapper(p->pw_name);
        }
-       /* 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)
+#ifndef CONFIG_ADDGROUP
+static inline void if_i_am_not_root(void)
+#else
+void if_i_am_not_root(void)
+#endif
 {
-       return geteuid();
+       if (geteuid()) {
+               bb_error_msg_and_die( "Only root may add a user or group to the system.");
+       }
 }
 
+#define SETPASS                                1
+#define MAKEHOME                       4
+
 /*
  * adduser will take a login_name as its first parameter.
  *
@@ -283,47 +252,36 @@ static inline uid_t i_am_not_root(void)
  * ________________________________________________________________________ */
 int adduser_main(int argc, char **argv)
 {
-       int i = 0;
-       char opt;
+       struct passwd pw;
        const char *login;
-       const char *gecos;
+       const char *gecos = default_gecos;
        const char *home = NULL;
-       const char *shell;
-
-       struct passwd pw;
+       const char *shell = DEFAULT_SHELL;
+       const char *usegroup = NULL;
+       int flags;
+       int setpass = 1;
+       int makehome = 1;
 
        /* init */
        if (argc < 2) {
-               show_usage();
+               bb_show_usage();
        }
-       gecos = default_gecos;
-       shell = default_shell;
-
        /* get args */
-       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;
-               }
+       flags = bb_getopt_ulflags(argc, argv, "h:g:s:G:DSH", &home, &gecos, &shell, &usegroup);
 
-       /* got root? */
-       if (i_am_not_root()) {
-               error_msg_and_die( "Only root may add a user or group to the system.");
+       if (flags & SETPASS) {
+               setpass = 0;
        }
+       if (flags & MAKEHOME) {
+               makehome = 0;
+       }
+
+       /* got root? */
+       if_i_am_not_root();
 
        /* get login */
        if (optind >= argc) {
-               error_msg_and_die( "no user specified");
+               bb_error_msg_and_die( "no user specified");
        }
        login = argv[optind];
 
@@ -333,7 +291,7 @@ int adduser_main(int argc, char **argv)
        }
 #ifdef CONFIG_FEATURE_SHADOWPASSWDS
        /* is /etc/shadow in use? */
-       shadow_enabled = (0 == access(SHADOW_FILE, F_OK));
+       shadow_enabled = (0 == access(bb_path_shadow_file, F_OK));
 #endif
 
        /* create a passwd struct */
@@ -345,8 +303,17 @@ int adduser_main(int argc, char **argv)
        pw.pw_dir = (char *)home;
        pw.pw_shell = (char *)shell;
 
+       if (usegroup) {
+               /* Add user to a group that already exists */
+               struct group *g;
+
+               g = getgrnam(usegroup);
+               if (g == NULL)
+                       bb_error_msg_and_die("group %s does not exist", usegroup);
+
+               pw.pw_gid = g->gr_gid;
+       }
+
        /* grand finale */
-       return adduser(PASSWD_FILE, &pw);
+       return adduser(bb_path_passwd_file, &pw, makehome, setpass);
 }
-
-/* $Id: adduser.c,v 1.1 2002/06/04 20:45:05 sandman Exp $ */