Initial public busybox upstream commit
[busybox4maemo] / loginutils / adduser.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * adduser - add users to /etc/passwd and /etc/shadow
4  *
5  * Copyright (C) 1999 by Lineo, inc. and John Beppu
6  * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org>
7  *
8  * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
9  */
10
11 #include "libbb.h"
12
13 #define OPT_DONT_SET_PASS  (1 << 4)
14 #define OPT_SYSTEM_ACCOUNT (1 << 5)
15 #define OPT_DONT_MAKE_HOME (1 << 6)
16
17
18 /* remix */
19 /* recoded such that the uid may be passed in *p */
20 static void passwd_study(struct passwd *p)
21 {
22         int max;
23
24         if (getpwnam(p->pw_name))
25                 bb_error_msg_and_die("login '%s' is in use", p->pw_name);
26
27         if (option_mask32 & OPT_SYSTEM_ACCOUNT) {
28                 p->pw_uid = 0;
29                 max = 999;
30         } else {
31                 p->pw_uid = 1000;
32                 max = 64999;
33         }
34
35         /* check for a free uid (and maybe gid) */
36         while (getpwuid(p->pw_uid) || (!p->pw_gid && getgrgid(p->pw_uid)))
37                 p->pw_uid++;
38
39         if (!p->pw_gid) {
40                 /* new gid = uid */
41                 p->pw_gid = p->pw_uid;
42                 if (getgrnam(p->pw_name))
43                         bb_error_msg_and_die("group name '%s' is in use", p->pw_name);
44         }
45
46         if (p->pw_uid > max)
47                 bb_error_msg_and_die("no free uids left");
48 }
49
50 static void addgroup_wrapper(struct passwd *p)
51 {
52         char *cmd;
53
54         cmd = xasprintf("addgroup -g %u '%s'", (unsigned)p->pw_gid, p->pw_name);
55         system(cmd);
56         free(cmd);
57 }
58
59 static void passwd_wrapper(const char *login) ATTRIBUTE_NORETURN;
60
61 static void passwd_wrapper(const char *login)
62 {
63         static const char prog[] ALIGN1 = "passwd";
64
65         BB_EXECLP(prog, prog, login, NULL);
66         bb_error_msg_and_die("cannot execute %s, you must set password manually", prog);
67 }
68
69 #if ENABLE_FEATURE_ADDUSER_LONG_OPTIONS
70 static const char adduser_longopts[] ALIGN1 =
71                 "home\0"                Required_argument "h"
72                 "gecos\0"               Required_argument "g"
73                 "shell\0"               Required_argument "s"
74                 "ingroup\0"             Required_argument "G"
75                 "disabled-password\0"   No_argument       "D"
76                 "empty-password\0"      No_argument       "D"
77                 "system\0"              No_argument       "S"
78                 "no-create-home\0"      No_argument       "H"
79                 ;
80 #endif
81
82 /*
83  * adduser will take a login_name as its first parameter.
84  * home, shell, gecos:
85  * can be customized via command-line parameters.
86  */
87 int adduser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
88 int adduser_main(int argc ATTRIBUTE_UNUSED, char **argv)
89 {
90         struct passwd pw;
91         const char *usegroup = NULL;
92         FILE *file;
93
94 #if ENABLE_FEATURE_ADDUSER_LONG_OPTIONS
95         applet_long_options = adduser_longopts;
96 #endif
97
98         /* got root? */
99         if (geteuid()) {
100                 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
101         }
102
103         pw.pw_gecos = (char *)"Linux User,,,";
104         pw.pw_shell = (char *)DEFAULT_SHELL;
105         pw.pw_dir = NULL;
106
107         /* exactly one non-option arg */
108         opt_complementary = "=1";
109         getopt32(argv, "h:g:s:G:DSH", &pw.pw_dir, &pw.pw_gecos, &pw.pw_shell, &usegroup);
110         argv += optind;
111
112         /* fill in the passwd struct */
113         pw.pw_name = argv[0];
114         die_if_bad_username(pw.pw_name);
115         if (!pw.pw_dir) {
116                 /* create string for $HOME if not specified already */
117                 pw.pw_dir = xasprintf("/home/%s", argv[0]);
118         }
119         pw.pw_passwd = (char *)"x";
120         pw.pw_gid = usegroup ? xgroup2gid(usegroup) : 0; /* exits on failure */
121
122         /* make sure everything is kosher and setup uid && maybe gid */
123         passwd_study(&pw);
124
125         /* add to passwd */
126         file = xfopen(bb_path_passwd_file, "a");
127         //fseek(file, 0, SEEK_END); /* paranoia, "a" should ensure that anyway */
128         if (putpwent(&pw, file) != 0) {
129                 bb_perror_nomsg_and_die();
130         }
131         /* do fclose even if !ENABLE_FEATURE_CLEAN_UP.
132          * We will exec passwd, files must be flushed & closed before that! */
133         fclose(file);
134
135 #if ENABLE_FEATURE_SHADOWPASSWDS
136         /* add to shadow if necessary */
137         file = fopen_or_warn(bb_path_shadow_file, "a");
138         if (file) {
139                 //fseek(file, 0, SEEK_END);
140                 fprintf(file, "%s:!:%u:0:99999:7:::\n",
141                                 pw.pw_name,             /* username */
142                                 (unsigned)(time(NULL) / 86400) /* sp->sp_lstchg */
143                                 /*0,*/                  /* sp->sp_min */
144                                 /*99999,*/              /* sp->sp_max */
145                                 /*7*/                   /* sp->sp_warn */
146                 );
147                 fclose(file);
148         }
149 #endif
150
151         /* add to group */
152         /* addgroup should be responsible for dealing w/ gshadow */
153         /* if using a pre-existing group, don't create one */
154         if (!usegroup)
155                 addgroup_wrapper(&pw);
156
157         /* Clear the umask for this process so it doesn't
158          * screw up the permissions on the mkdir and chown. */
159         umask(0);
160         if (!(option_mask32 & OPT_DONT_MAKE_HOME)) {
161                 /* Set the owner and group so it is owned by the new user,
162                    then fix up the permissions to 2755. Can't do it before
163                    since chown will clear the setgid bit */
164                 if (mkdir(pw.pw_dir, 0755)
165                  || chown(pw.pw_dir, pw.pw_uid, pw.pw_gid)
166                  || chmod(pw.pw_dir, 02755) /* set setgid bit on homedir */
167                 ) {
168                         bb_simple_perror_msg(pw.pw_dir);
169                 }
170         }
171
172         if (!(option_mask32 & OPT_DONT_SET_PASS)) {
173                 /* interactively set passwd */
174                 passwd_wrapper(pw.pw_name);
175         }
176
177         return 0;
178 }