diff options
Diffstat (limited to 'loginutils')
-rw-r--r-- | loginutils/addgroup.c | 2 | ||||
-rw-r--r-- | loginutils/adduser.c | 196 | ||||
-rw-r--r-- | loginutils/passwd.c | 27 | ||||
-rw-r--r-- | loginutils/sulogin.c | 13 |
4 files changed, 102 insertions, 136 deletions
diff --git a/loginutils/addgroup.c b/loginutils/addgroup.c index 9b2c6790f..31e507045 100644 --- a/loginutils/addgroup.c +++ b/loginutils/addgroup.c | |||
@@ -56,7 +56,7 @@ static void new_group(char *group, gid_t gid) | |||
56 | /* add entry to group */ | 56 | /* add entry to group */ |
57 | file = xfopen(bb_path_group_file, "a"); | 57 | file = xfopen(bb_path_group_file, "a"); |
58 | /* group:passwd:gid:userlist */ | 58 | /* group:passwd:gid:userlist */ |
59 | fprintf(file, "%s:x:%d:\n", group, gr.gr_gid); | 59 | fprintf(file, "%s:x:%u:\n", group, (unsigned)gr.gr_gid); |
60 | if (ENABLE_FEATURE_CLEAN_UP) | 60 | if (ENABLE_FEATURE_CLEAN_UP) |
61 | fclose(file); | 61 | fclose(file); |
62 | #if ENABLE_FEATURE_SHADOWPASSWDS | 62 | #if ENABLE_FEATURE_SHADOWPASSWDS |
diff --git a/loginutils/adduser.c b/loginutils/adduser.c index 5c624285f..e91417ad5 100644 --- a/loginutils/adduser.c +++ b/loginutils/adduser.c | |||
@@ -11,68 +11,47 @@ | |||
11 | #include "libbb.h" | 11 | #include "libbb.h" |
12 | 12 | ||
13 | #define OPT_DONT_SET_PASS (1 << 4) | 13 | #define OPT_DONT_SET_PASS (1 << 4) |
14 | #define OPT_SYSTEM_ACCOUNT (1 << 5) | ||
14 | #define OPT_DONT_MAKE_HOME (1 << 6) | 15 | #define OPT_DONT_MAKE_HOME (1 << 6) |
15 | 16 | ||
16 | 17 | ||
17 | /* remix */ | 18 | /* remix */ |
18 | /* EDR recoded such that the uid may be passed in *p */ | 19 | /* recoded such that the uid may be passed in *p */ |
19 | static int passwd_study(const char *filename, struct passwd *p) | 20 | static void passwd_study(struct passwd *p) |
20 | { | 21 | { |
21 | enum { min = 500, max = 65000 }; | 22 | int max; |
22 | FILE *passwd; | ||
23 | /* We are using reentrant fgetpwent_r() in order to avoid | ||
24 | * pulling in static buffers from libc (think static build here) */ | ||
25 | char buffer[256]; | ||
26 | struct passwd pw; | ||
27 | struct passwd *result; | ||
28 | |||
29 | passwd = xfopen(filename, "r"); | ||
30 | |||
31 | /* EDR if uid is out of bounds, set to min */ | ||
32 | if ((p->pw_uid > max) || (p->pw_uid < min)) | ||
33 | p->pw_uid = min; | ||
34 | |||
35 | /* stuff to do: | ||
36 | * make sure login isn't taken; | ||
37 | * find free uid and gid; | ||
38 | */ | ||
39 | while (!fgetpwent_r(passwd, &pw, buffer, sizeof(buffer), &result)) { | ||
40 | if (strcmp(pw.pw_name, p->pw_name) == 0) { | ||
41 | /* return 0; */ | ||
42 | return 1; | ||
43 | } | ||
44 | if ((pw.pw_uid >= p->pw_uid) && (pw.pw_uid < max) | ||
45 | && (pw.pw_uid >= min)) { | ||
46 | p->pw_uid = pw.pw_uid + 1; | ||
47 | } | ||
48 | } | ||
49 | 23 | ||
50 | if (p->pw_gid == 0) { | 24 | if (getpwnam(p->pw_name)) |
51 | /* EDR check for an already existing gid */ | 25 | bb_error_msg_and_die("login '%s' is in use", p->pw_name); |
52 | while (getgrgid(p->pw_uid) != NULL) | ||
53 | p->pw_uid++; | ||
54 | 26 | ||
55 | /* EDR also check for an existing group definition */ | 27 | if (option_mask32 & OPT_SYSTEM_ACCOUNT) { |
56 | if (getgrnam(p->pw_name) != NULL) | 28 | p->pw_uid = 0; |
57 | return 3; | 29 | max = 999; |
30 | } else { | ||
31 | p->pw_uid = 1000; | ||
32 | max = 64999; | ||
33 | } | ||
58 | 34 | ||
59 | /* EDR create new gid always = uid */ | 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 */ | ||
60 | p->pw_gid = p->pw_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); | ||
61 | } | 44 | } |
62 | 45 | ||
63 | /* EDR bounds check */ | 46 | if (p->pw_uid > max) |
64 | if ((p->pw_uid > max) || (p->pw_uid < min)) | 47 | bb_error_msg_and_die("no free uids left"); |
65 | return 2; | ||
66 | |||
67 | /* return 1; */ | ||
68 | return 0; | ||
69 | } | 48 | } |
70 | 49 | ||
71 | static void addgroup_wrapper(struct passwd *p) | 50 | static void addgroup_wrapper(struct passwd *p) |
72 | { | 51 | { |
73 | char *cmd; | 52 | char *cmd; |
74 | 53 | ||
75 | cmd = xasprintf("addgroup -g %d \"%s\"", p->pw_gid, p->pw_name); | 54 | cmd = xasprintf("addgroup -g %u '%s'", (unsigned)p->pw_gid, p->pw_name); |
76 | system(cmd); | 55 | system(cmd); |
77 | free(cmd); | 56 | free(cmd); |
78 | } | 57 | } |
@@ -84,33 +63,54 @@ static void passwd_wrapper(const char *login) | |||
84 | static const char prog[] ALIGN1 = "passwd"; | 63 | static const char prog[] ALIGN1 = "passwd"; |
85 | 64 | ||
86 | BB_EXECLP(prog, prog, login, NULL); | 65 | BB_EXECLP(prog, prog, login, NULL); |
87 | bb_error_msg_and_die("failed to execute '%s', you must set the password for '%s' manually", prog, login); | 66 | bb_error_msg_and_die("cannot execute %s, you must set password manually", prog); |
88 | } | 67 | } |
89 | 68 | ||
90 | /* putpwent(3) remix */ | 69 | /* |
91 | static int adduser(struct passwd *p) | 70 | * adduser will take a login_name as its first parameter. |
71 | * home, shell, gecos: | ||
72 | * can be customized via command-line parameters. | ||
73 | */ | ||
74 | int adduser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
75 | int adduser_main(int argc, char **argv) | ||
92 | { | 76 | { |
77 | struct passwd pw; | ||
78 | const char *usegroup = NULL; | ||
93 | FILE *file; | 79 | FILE *file; |
94 | int addgroup = !p->pw_gid; | ||
95 | 80 | ||
96 | /* make sure everything is kosher and setup uid && gid */ | 81 | /* got root? */ |
97 | file = xfopen(bb_path_passwd_file, "a"); | 82 | if (geteuid()) { |
98 | fseek(file, 0, SEEK_END); | 83 | bb_error_msg_and_die(bb_msg_perm_denied_are_you_root); |
99 | |||
100 | switch (passwd_study(bb_path_passwd_file, p)) { | ||
101 | case 1: | ||
102 | bb_error_msg_and_die("%s: login already in use", p->pw_name); | ||
103 | case 2: | ||
104 | bb_error_msg_and_die("illegal uid or no uids left"); | ||
105 | case 3: | ||
106 | bb_error_msg_and_die("%s: group name already in use", p->pw_name); | ||
107 | } | 84 | } |
108 | 85 | ||
86 | pw.pw_gecos = (char *)"Linux User,,,"; | ||
87 | pw.pw_shell = (char *)DEFAULT_SHELL; | ||
88 | pw.pw_dir = NULL; | ||
89 | |||
90 | /* exactly one non-option arg */ | ||
91 | opt_complementary = "=1"; | ||
92 | getopt32(argv, "h:g:s:G:DSH", &pw.pw_dir, &pw.pw_gecos, &pw.pw_shell, &usegroup); | ||
93 | argv += optind; | ||
94 | |||
95 | /* fill in the passwd struct */ | ||
96 | pw.pw_name = argv[0]; | ||
97 | if (!pw.pw_dir) { | ||
98 | /* create string for $HOME if not specified already */ | ||
99 | pw.pw_dir = xasprintf("/home/%s", argv[0]); | ||
100 | } | ||
101 | pw.pw_passwd = (char *)"x"; | ||
102 | pw.pw_gid = usegroup ? xgroup2gid(usegroup) : 0; /* exits on failure */ | ||
103 | |||
104 | /* make sure everything is kosher and setup uid && maybe gid */ | ||
105 | passwd_study(&pw); | ||
106 | |||
109 | /* add to passwd */ | 107 | /* add to passwd */ |
110 | if (putpwent(p, file) == -1) { | 108 | file = xfopen(bb_path_passwd_file, "a"); |
109 | //fseek(file, 0, SEEK_END); /* paranoia, "a" should ensure that anyway */ | ||
110 | if (putpwent(&pw, file) != 0) { | ||
111 | bb_perror_nomsg_and_die(); | 111 | bb_perror_nomsg_and_die(); |
112 | } | 112 | } |
113 | /* Do fclose even if !ENABLE_FEATURE_CLEAN_UP. | 113 | /* do fclose even if !ENABLE_FEATURE_CLEAN_UP. |
114 | * We will exec passwd, files must be flushed & closed before that! */ | 114 | * We will exec passwd, files must be flushed & closed before that! */ |
115 | fclose(file); | 115 | fclose(file); |
116 | 116 | ||
@@ -118,13 +118,14 @@ static int adduser(struct passwd *p) | |||
118 | /* add to shadow if necessary */ | 118 | /* add to shadow if necessary */ |
119 | file = fopen_or_warn(bb_path_shadow_file, "a"); | 119 | file = fopen_or_warn(bb_path_shadow_file, "a"); |
120 | if (file) { | 120 | if (file) { |
121 | fseek(file, 0, SEEK_END); | 121 | //fseek(file, 0, SEEK_END); |
122 | fprintf(file, "%s:!:%ld:%d:%d:%d:::\n", | 122 | fprintf(file, "%s:!:%u:0:99999:7:::\n", |
123 | p->pw_name, /* username */ | 123 | pw.pw_name, /* username */ |
124 | time(NULL) / 86400, /* sp->sp_lstchg */ | 124 | (unsigned)(time(NULL) / 86400) /* sp->sp_lstchg */ |
125 | 0, /* sp->sp_min */ | 125 | /*0,*/ /* sp->sp_min */ |
126 | 99999, /* sp->sp_max */ | 126 | /*99999,*/ /* sp->sp_max */ |
127 | 7); /* sp->sp_warn */ | 127 | /*7*/ /* sp->sp_warn */ |
128 | ); | ||
128 | fclose(file); | 129 | fclose(file); |
129 | } | 130 | } |
130 | #endif | 131 | #endif |
@@ -132,7 +133,8 @@ static int adduser(struct passwd *p) | |||
132 | /* add to group */ | 133 | /* add to group */ |
133 | /* addgroup should be responsible for dealing w/ gshadow */ | 134 | /* addgroup should be responsible for dealing w/ gshadow */ |
134 | /* if using a pre-existing group, don't create one */ | 135 | /* if using a pre-existing group, don't create one */ |
135 | if (addgroup) addgroup_wrapper(p); | 136 | if (!usegroup) |
137 | addgroup_wrapper(&pw); | ||
136 | 138 | ||
137 | /* Clear the umask for this process so it doesn't | 139 | /* Clear the umask for this process so it doesn't |
138 | * screw up the permissions on the mkdir and chown. */ | 140 | * screw up the permissions on the mkdir and chown. */ |
@@ -141,60 +143,18 @@ static int adduser(struct passwd *p) | |||
141 | /* Set the owner and group so it is owned by the new user, | 143 | /* Set the owner and group so it is owned by the new user, |
142 | then fix up the permissions to 2755. Can't do it before | 144 | then fix up the permissions to 2755. Can't do it before |
143 | since chown will clear the setgid bit */ | 145 | since chown will clear the setgid bit */ |
144 | if (mkdir(p->pw_dir, 0755) | 146 | if (mkdir(pw.pw_dir, 0755) |
145 | || chown(p->pw_dir, p->pw_uid, p->pw_gid) | 147 | || chown(pw.pw_dir, pw.pw_uid, pw.pw_gid) |
146 | || chmod(p->pw_dir, 02755)) { | 148 | || chmod(pw.pw_dir, 02755) /* set setgid bit on homedir */ |
147 | bb_simple_perror_msg(p->pw_dir); | 149 | ) { |
150 | bb_simple_perror_msg(pw.pw_dir); | ||
148 | } | 151 | } |
149 | } | 152 | } |
150 | 153 | ||
151 | if (!(option_mask32 & OPT_DONT_SET_PASS)) { | 154 | if (!(option_mask32 & OPT_DONT_SET_PASS)) { |
152 | /* interactively set passwd */ | 155 | /* interactively set passwd */ |
153 | passwd_wrapper(p->pw_name); | 156 | passwd_wrapper(pw.pw_name); |
154 | } | 157 | } |
155 | 158 | ||
156 | return 0; | 159 | return 0; |
157 | } | 160 | } |
158 | |||
159 | /* | ||
160 | * adduser will take a login_name as its first parameter. | ||
161 | * | ||
162 | * home | ||
163 | * shell | ||
164 | * gecos | ||
165 | * | ||
166 | * can be customized via command-line parameters. | ||
167 | */ | ||
168 | int adduser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
169 | int adduser_main(int argc, char **argv) | ||
170 | { | ||
171 | struct passwd pw; | ||
172 | const char *usegroup = NULL; | ||
173 | |||
174 | /* got root? */ | ||
175 | if (geteuid()) { | ||
176 | bb_error_msg_and_die(bb_msg_perm_denied_are_you_root); | ||
177 | } | ||
178 | |||
179 | pw.pw_gecos = (char *)"Linux User,,,"; | ||
180 | pw.pw_shell = (char *)DEFAULT_SHELL; | ||
181 | pw.pw_dir = NULL; | ||
182 | |||
183 | /* exactly one non-option arg */ | ||
184 | opt_complementary = "=1"; | ||
185 | getopt32(argv, "h:g:s:G:DSH", &pw.pw_dir, &pw.pw_gecos, &pw.pw_shell, &usegroup); | ||
186 | argv += optind; | ||
187 | |||
188 | /* create a passwd struct */ | ||
189 | pw.pw_name = argv[0]; | ||
190 | if (!pw.pw_dir) { | ||
191 | /* create string for $HOME if not specified already */ | ||
192 | pw.pw_dir = xasprintf("/home/%s", argv[0]); | ||
193 | } | ||
194 | pw.pw_passwd = (char *)"x"; | ||
195 | pw.pw_uid = 0; | ||
196 | pw.pw_gid = usegroup ? xgroup2gid(usegroup) : 0; /* exits on failure */ | ||
197 | |||
198 | /* grand finale */ | ||
199 | return adduser(&pw); | ||
200 | } | ||
diff --git a/loginutils/passwd.c b/loginutils/passwd.c index 99afde223..0842b71bf 100644 --- a/loginutils/passwd.c +++ b/loginutils/passwd.c | |||
@@ -93,11 +93,9 @@ int passwd_main(int argc, char **argv) | |||
93 | uid_t myuid; | 93 | uid_t myuid; |
94 | struct rlimit rlimit_fsize; | 94 | struct rlimit rlimit_fsize; |
95 | char c; | 95 | char c; |
96 | |||
97 | #if ENABLE_FEATURE_SHADOWPASSWDS | 96 | #if ENABLE_FEATURE_SHADOWPASSWDS |
98 | /* Using _r function to avoid pulling in static buffers */ | 97 | /* Using _r function to avoid pulling in static buffers */ |
99 | struct spwd spw; | 98 | struct spwd spw; |
100 | struct spwd *result; | ||
101 | char buffer[256]; | 99 | char buffer[256]; |
102 | #endif | 100 | #endif |
103 | 101 | ||
@@ -128,16 +126,19 @@ int passwd_main(int argc, char **argv) | |||
128 | } | 126 | } |
129 | 127 | ||
130 | #if ENABLE_FEATURE_SHADOWPASSWDS | 128 | #if ENABLE_FEATURE_SHADOWPASSWDS |
131 | /* getspnam_r() can lie! Even if user isn't in shadow, it can | 129 | { |
132 | * return success (pwd field was seen set to "!" in this case) */ | 130 | /* getspnam_r may return 0 yet set result to NULL. |
133 | if (getspnam_r(pw->pw_name, &spw, buffer, sizeof(buffer), &result) | 131 | * At least glibc 2.4 does this. Be extra paranoid here. */ |
134 | || LONE_CHAR(spw.sp_pwdp, '!')) { | 132 | struct spwd *result = NULL; |
135 | /* LOGMODE_BOTH */ | 133 | if (getspnam_r(pw->pw_name, &spw, buffer, sizeof(buffer), &result) |
136 | bb_error_msg("no record of %s in %s, using %s", | 134 | || !result || strcmp(result->sp_namp, pw->pw_name) != 0) { |
137 | name, bb_path_shadow_file, | 135 | /* LOGMODE_BOTH */ |
138 | bb_path_passwd_file); | 136 | bb_error_msg("no record of %s in %s, using %s", |
139 | } else { | 137 | name, bb_path_shadow_file, |
140 | pw->pw_passwd = spw.sp_pwdp; | 138 | bb_path_passwd_file); |
139 | } else { | ||
140 | pw->pw_passwd = result->sp_pwdp; | ||
141 | } | ||
141 | } | 142 | } |
142 | #endif | 143 | #endif |
143 | 144 | ||
@@ -161,7 +162,7 @@ int passwd_main(int argc, char **argv) | |||
161 | newp = xasprintf("!%s", pw->pw_passwd); | 162 | newp = xasprintf("!%s", pw->pw_passwd); |
162 | } else if (opt & OPT_unlock) { | 163 | } else if (opt & OPT_unlock) { |
163 | if (c) goto skip; /* not '!' */ | 164 | if (c) goto skip; /* not '!' */ |
164 | /* pw->pw_passwd pints to static storage, | 165 | /* pw->pw_passwd points to static storage, |
165 | * strdup'ing to avoid nasty surprizes */ | 166 | * strdup'ing to avoid nasty surprizes */ |
166 | newp = xstrdup(&pw->pw_passwd[1]); | 167 | newp = xstrdup(&pw->pw_passwd[1]); |
167 | } else if (opt & OPT_delete) { | 168 | } else if (opt & OPT_delete) { |
diff --git a/loginutils/sulogin.c b/loginutils/sulogin.c index f633fbbf1..f1545b78f 100644 --- a/loginutils/sulogin.c +++ b/loginutils/sulogin.c | |||
@@ -44,7 +44,6 @@ int sulogin_main(int argc, char **argv) | |||
44 | /* Using _r function to avoid pulling in static buffers */ | 44 | /* Using _r function to avoid pulling in static buffers */ |
45 | char buffer[256]; | 45 | char buffer[256]; |
46 | struct spwd spw; | 46 | struct spwd spw; |
47 | struct spwd *result; | ||
48 | #endif | 47 | #endif |
49 | 48 | ||
50 | logmode = LOGMODE_BOTH; | 49 | logmode = LOGMODE_BOTH; |
@@ -83,10 +82,16 @@ int sulogin_main(int argc, char **argv) | |||
83 | } | 82 | } |
84 | 83 | ||
85 | #if ENABLE_FEATURE_SHADOWPASSWDS | 84 | #if ENABLE_FEATURE_SHADOWPASSWDS |
86 | if (getspnam_r(pwd->pw_name, &spw, buffer, sizeof(buffer), &result)) { | 85 | { |
87 | goto auth_error; | 86 | /* getspnam_r may return 0 yet set result to NULL. |
87 | * At least glibc 2.4 does this. Be extra paranoid here. */ | ||
88 | struct spwd *result = NULL; | ||
89 | int r = getspnam_r(pwd->pw_name, &spw, buffer, sizeof(buffer), &result); | ||
90 | if (r || !result) { | ||
91 | goto auth_error; | ||
92 | } | ||
93 | pwd->pw_passwd = result->sp_pwdp; | ||
88 | } | 94 | } |
89 | pwd->pw_passwd = spw.sp_pwdp; | ||
90 | #endif | 95 | #endif |
91 | 96 | ||
92 | while (1) { | 97 | while (1) { |