8 |
* Licensed under the GPL v2 or later, see the file LICENSE in this tarball. |
* Licensed under the GPL v2 or later, see the file LICENSE in this tarball. |
9 |
*/ |
*/ |
10 |
|
|
11 |
#include "busybox.h" |
#include "libbb.h" |
12 |
|
|
13 |
#define DONT_SET_PASS (1 << 4) |
#define OPT_DONT_SET_PASS (1 << 4) |
14 |
#define DONT_MAKE_HOME (1 << 6) |
#define OPT_SYSTEM_ACCOUNT (1 << 5) |
15 |
|
#define OPT_DONT_MAKE_HOME (1 << 6) |
16 |
|
|
17 |
|
|
18 |
/* remix */ |
/* remix */ |
19 |
/* EDR recoded such that the uid may be passed in *p */ |
/* recoded such that the uid may be passed in *p */ |
20 |
static int passwd_study(const char *filename, struct passwd *p) |
static void passwd_study(struct passwd *p) |
21 |
{ |
{ |
22 |
struct passwd *pw; |
int max; |
|
FILE *passwd; |
|
23 |
|
|
24 |
const int min = 500; |
if (getpwnam(p->pw_name)) |
25 |
const int max = 65000; |
bb_error_msg_and_die("login '%s' is in use", p->pw_name); |
26 |
|
|
27 |
passwd = xfopen(filename, "r"); |
if (option_mask32 & OPT_SYSTEM_ACCOUNT) { |
28 |
|
p->pw_uid = 0; |
29 |
/* EDR if uid is out of bounds, set to min */ |
max = 999; |
30 |
if ((p->pw_uid > max) || (p->pw_uid < min)) |
} else { |
31 |
p->pw_uid = min; |
p->pw_uid = 1000; |
32 |
|
max = 64999; |
|
/* 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; |
|
|
} |
|
33 |
} |
} |
34 |
|
|
35 |
if (p->pw_gid == 0) { |
/* check for a free uid (and maybe gid) */ |
36 |
/* EDR check for an already existing gid */ |
while (getpwuid(p->pw_uid) || (!p->pw_gid && getgrgid(p->pw_uid))) |
37 |
while (getgrgid(p->pw_uid) != NULL) |
p->pw_uid++; |
|
p->pw_uid++; |
|
|
|
|
|
/* EDR also check for an existing group definition */ |
|
|
if (getgrnam(p->pw_name) != NULL) |
|
|
return 3; |
|
38 |
|
|
39 |
/* EDR create new gid always = uid */ |
if (!p->pw_gid) { |
40 |
|
/* new gid = uid */ |
41 |
p->pw_gid = p->pw_uid; |
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 |
/* EDR bounds check */ |
if (p->pw_uid > max) |
47 |
if ((p->pw_uid > max) || (p->pw_uid < min)) |
bb_error_msg_and_die("no free uids left"); |
|
return 2; |
|
|
|
|
|
/* return 1; */ |
|
|
return 0; |
|
48 |
} |
} |
49 |
|
|
50 |
static void addgroup_wrapper(struct passwd *p) |
static void addgroup_wrapper(struct passwd *p) |
51 |
{ |
{ |
52 |
char *cmd; |
char *cmd; |
53 |
|
|
54 |
cmd = xasprintf("addgroup -g %d \"%s\"", p->pw_gid, p->pw_name); |
cmd = xasprintf("addgroup -g %u '%s'", (unsigned)p->pw_gid, p->pw_name); |
55 |
system(cmd); |
system(cmd); |
56 |
free(cmd); |
free(cmd); |
57 |
} |
} |
58 |
|
|
59 |
static void passwd_wrapper(const char *login) ATTRIBUTE_NORETURN; |
static void passwd_wrapper(const char *login) NORETURN; |
60 |
|
|
61 |
static void passwd_wrapper(const char *login) |
static void passwd_wrapper(const char *login) |
62 |
{ |
{ |
63 |
static const char prog[] = "passwd"; |
static const char prog[] ALIGN1 = "passwd"; |
64 |
execlp(prog, prog, login, NULL); |
|
65 |
bb_error_msg_and_die("failed to execute '%s', you must set the password for '%s' manually", prog, login); |
BB_EXECLP(prog, prog, login, NULL); |
66 |
|
bb_error_msg_and_die("cannot execute %s, you must set password manually", prog); |
67 |
} |
} |
68 |
|
|
69 |
/* putpwent(3) remix */ |
#if ENABLE_FEATURE_ADDUSER_LONG_OPTIONS |
70 |
static int adduser(struct passwd *p, unsigned long flags) |
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 UNUSED_PARAM, char **argv) |
89 |
{ |
{ |
90 |
|
struct passwd pw; |
91 |
|
const char *usegroup = NULL; |
92 |
FILE *file; |
FILE *file; |
|
int addgroup = !p->pw_gid; |
|
93 |
|
|
94 |
/* make sure everything is kosher and setup uid && gid */ |
#if ENABLE_FEATURE_ADDUSER_LONG_OPTIONS |
95 |
file = xfopen(bb_path_passwd_file, "a"); |
applet_long_options = adduser_longopts; |
96 |
fseek(file, 0, SEEK_END); |
#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 |
switch (passwd_study(bb_path_passwd_file, p)) { |
/* make sure everything is kosher and setup uid && maybe gid */ |
123 |
case 1: |
passwd_study(&pw); |
|
bb_error_msg_and_die("%s: login already in use", p->pw_name); |
|
|
case 2: |
|
|
bb_error_msg_and_die("illegal uid or no uids left"); |
|
|
case 3: |
|
|
bb_error_msg_and_die("%s: group name already in use", p->pw_name); |
|
|
} |
|
124 |
|
|
125 |
/* add to passwd */ |
/* add to passwd */ |
126 |
if (putpwent(p, file) == -1) { |
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(); |
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); |
fclose(file); |
134 |
|
|
135 |
#if ENABLE_FEATURE_SHADOWPASSWDS |
#if ENABLE_FEATURE_SHADOWPASSWDS |
136 |
/* add to shadow if necessary */ |
/* add to shadow if necessary */ |
137 |
file = xfopen(bb_path_shadow_file, "a"); |
file = fopen_or_warn(bb_path_shadow_file, "a"); |
138 |
fseek(file, 0, SEEK_END); |
if (file) { |
139 |
fprintf(file, "%s:!:%ld:%d:%d:%d:::\n", |
//fseek(file, 0, SEEK_END); |
140 |
p->pw_name, /* username */ |
fprintf(file, "%s:!:%u:0:99999:7:::\n", |
141 |
time(NULL) / 86400, /* sp->sp_lstchg */ |
pw.pw_name, /* username */ |
142 |
0, /* sp->sp_min */ |
(unsigned)(time(NULL) / 86400) /* sp->sp_lstchg */ |
143 |
99999, /* sp->sp_max */ |
/*0,*/ /* sp->sp_min */ |
144 |
7); /* sp->sp_warn */ |
/*99999,*/ /* sp->sp_max */ |
145 |
fclose(file); |
/*7*/ /* sp->sp_warn */ |
146 |
|
); |
147 |
|
fclose(file); |
148 |
|
} |
149 |
#endif |
#endif |
150 |
|
|
151 |
/* add to group */ |
/* add to group */ |
152 |
/* addgroup should be responsible for dealing w/ gshadow */ |
/* addgroup should be responsible for dealing w/ gshadow */ |
153 |
/* if using a pre-existing group, don't create one */ |
/* if using a pre-existing group, don't create one */ |
154 |
if (addgroup) addgroup_wrapper(p); |
if (!usegroup) |
155 |
|
addgroup_wrapper(&pw); |
156 |
|
|
157 |
/* Clear the umask for this process so it doesn't |
/* Clear the umask for this process so it doesn't |
158 |
* * screw up the permissions on the mkdir and chown. */ |
* screw up the permissions on the mkdir and chown. */ |
159 |
umask(0); |
umask(0); |
160 |
if (!(flags & DONT_MAKE_HOME)) { |
if (!(option_mask32 & OPT_DONT_MAKE_HOME)) { |
161 |
/* Set the owner and group so it is owned by the new user, |
/* 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 |
then fix up the permissions to 2755. Can't do it before |
163 |
since chown will clear the setgid bit */ |
since chown will clear the setgid bit */ |
164 |
if (mkdir(p->pw_dir, 0755) |
if (mkdir(pw.pw_dir, 0755) |
165 |
|| chown(p->pw_dir, p->pw_uid, p->pw_gid) |
|| chown(pw.pw_dir, pw.pw_uid, pw.pw_gid) |
166 |
|| chmod(p->pw_dir, 02755)) { |
|| chmod(pw.pw_dir, 02755) /* set setgid bit on homedir */ |
167 |
bb_perror_msg("%s", p->pw_dir); |
) { |
168 |
|
bb_simple_perror_msg(pw.pw_dir); |
169 |
} |
} |
170 |
} |
} |
171 |
|
|
172 |
if (!(flags & DONT_SET_PASS)) { |
if (!(option_mask32 & OPT_DONT_SET_PASS)) { |
173 |
/* interactively set passwd */ |
/* interactively set passwd */ |
174 |
passwd_wrapper(p->pw_name); |
passwd_wrapper(pw.pw_name); |
175 |
} |
} |
176 |
|
|
177 |
return 0; |
return 0; |
178 |
} |
} |
|
|
|
|
/* |
|
|
* 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) |
|
|
{ |
|
|
struct passwd pw; |
|
|
const char *usegroup = NULL; |
|
|
unsigned long flags; |
|
|
|
|
|
/* got root? */ |
|
|
if (geteuid()) { |
|
|
bb_error_msg_and_die(bb_msg_perm_denied_are_you_root); |
|
|
} |
|
|
|
|
|
pw.pw_gecos = "Linux User,,,"; |
|
|
pw.pw_shell = (char *)DEFAULT_SHELL; |
|
|
pw.pw_dir = NULL; |
|
|
|
|
|
/* check for min, max and missing args and exit on error */ |
|
|
opt_complementary = "-1:?1:?"; |
|
|
flags = getopt32(argc, argv, "h:g:s:G:DSH", &pw.pw_dir, &pw.pw_gecos, &pw.pw_shell, &usegroup); |
|
|
|
|
|
/* create string for $HOME if not specified already */ |
|
|
if (!pw.pw_dir) { |
|
|
snprintf(bb_common_bufsiz1, BUFSIZ, "/home/%s", argv[optind]); |
|
|
pw.pw_dir = bb_common_bufsiz1; |
|
|
} |
|
|
|
|
|
/* create a passwd struct */ |
|
|
pw.pw_name = argv[optind]; |
|
|
pw.pw_passwd = "x"; |
|
|
pw.pw_uid = 0; |
|
|
pw.pw_gid = usegroup ? xgroup2gid(usegroup) : 0; /* exits on failure */ |
|
|
|
|
|
/* grand finale */ |
|
|
return adduser(&pw, flags); |
|
|
} |
|